diff --git a/.env.example b/.env.example index 9dcafbe..23f4733 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ NEXT_PUBLIC_API="https://laconapi.vercel.app" -NEXT_PUBLIC_CMS_ACCESS_TOKEN="280138ffd0e878d599892fc674d85a" \ No newline at end of file +NEXT_PUBLIC_CMS_ACCESS_TOKEN="280138ffd0e878d599892fc674d85a" +NEXT_PUBLIC_DATOCMS_BYPASS_TYPE="local_json" \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100755 new mode 100644 diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/aboutPageAllTeamsQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/aboutPageAllTeamsQuery.js new file mode 100644 index 0000000..9af52f2 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/aboutPageAllTeamsQuery.js @@ -0,0 +1,14 @@ +query MyQuery { + allTeams { + memberGithub + memberLinkedin + memberName + memberPosition + memberTeam + memberTwitter + id + memberImage { + url + } + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/aboutPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/aboutPageQuery.js new file mode 100644 index 0000000..a5d8c31 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/aboutPageQuery.js @@ -0,0 +1,25 @@ +query MyQuery { + aboutPage { + heroCtaLabel + heroCtaLink + heroDescB01 + heroDescB02 + heroHeading + heroTitle + teamHeading + teamDescB01 + teamDescB02 + whitepaperCtaLink + whitepaperCtaText + whitepaperHeading + whitepaperImage { + url + } + whitepaperLine + roadmapHeading + roadmapQuarter01 + roadmapQuarter02 + roadmapQuarter03 + roadmapQuarter04 + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllAuthorsQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllAuthorsQuery.js new file mode 100644 index 0000000..0f6c66a --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllAuthorsQuery.js @@ -0,0 +1,6 @@ +query MyQuery { + allAuthors { + id + name + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllBlogPostsQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllBlogPostsQuery.js new file mode 100644 index 0000000..f68bfb1 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllBlogPostsQuery.js @@ -0,0 +1,50 @@ +query MyQuery { + allBlogPosts { + slug + title + date + category { + id + slug + title + } + author { + id + name + } + image { + url + } + content { + blocks { + ... on Callout02Record { + id + text + } + ... on Callout01Record { + id + text + } + ... on CtaRecord { + id + label + url + } + ... on ImageRecord { + id + title + } + } + links + value + } + featured + id + _status + _seoMetaTags(locale: en) { + attributes + content + tag + } + } +} diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllCategoriesQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllCategoriesQuery.js new file mode 100644 index 0000000..8ec0bd3 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/blogAllCategoriesQuery.js @@ -0,0 +1,7 @@ +query MyQuery { + allCategories { + id + slug + title + } +} diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/careerPageAllPositionsQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/careerPageAllPositionsQuery.js new file mode 100644 index 0000000..367c9b4 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/careerPageAllPositionsQuery.js @@ -0,0 +1,8 @@ +query MyQuery { + allPositions { + id + positionLink + positionName + positionTeam + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/careersPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/careersPageQuery.js new file mode 100644 index 0000000..d127630 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/careersPageQuery.js @@ -0,0 +1,71 @@ +query MyQuery { + careersPage { + heroHeading + heroLine + valuesHeading + valuesPoint01Head + valuesPoint01Image { + url + } + valuesPoint01ImageLight { + url + } + valuesPoint01Line + valuesPoint02Head + valuesPoint02Image { + url + } + valuesPoint02ImageLight { + url + } + valuesPoint02Line + valuesPoint03Head + valuesPoint03Image { + url + } + valuesPoint03ImageLight { + url + } + valuesPoint03Line + valuesPoint04Head + valuesPoint04Image { + url + } + valuesPoint04ImageLight { + url + } + valuesPoint04Line + whyHeading + whyPoint01Img { + url + } + whyPoint01Line + whyPoint02Img { + url + } + whyPoint02Line + whyPoint03Img { + url + } + whyPoint03Line + whyPoint04Img { + url + } + whyPoint04Line + whyPoint05Img { + url + } + whyPoint05Line + whyPoint06Img { + url + } + whyPoint06Line + whyHeadNumber + positionsHeading + positionsHeadingNumber + positionsLinkLabel + positionsXylmHead + positionsDeepstackHead + positionsCercHead + } +} diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/communityPageAllEventsQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/communityPageAllEventsQuery.js new file mode 100644 index 0000000..013f7f2 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/communityPageAllEventsQuery.js @@ -0,0 +1,14 @@ +query MyQuery { + allEvents { + title + eventDesc + eventStartdate + eventEnddate + eventLocation + id + image { + url + } + link + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/communityPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/communityPageQuery.js new file mode 100644 index 0000000..6304c6c --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/communityPageQuery.js @@ -0,0 +1,29 @@ +query MyQuery { + communityPage { + heroButton01 + heroButton02 + heroDescription + heroHeading + heroB01Link + heroB02Link + eventsHeading + eventsDescription + socialsHeading + socialsHeadingAlt + socialsLine + socialsImage { + url + } + socialsImageLight { + url + } + socialsTwitter + socialsDiscord + socialsYoutube + socialsReddit + socialsTelegram + socialsLinkedin + socialsFacebook + socialsInstagram + } +} diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/contactPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/contactPageQuery.js new file mode 100644 index 0000000..b6b4408 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/contactPageQuery.js @@ -0,0 +1,18 @@ +query MyQuery { + contactPage { + heroHeading + heroLine + heroLink + heroMobileLinkLabel + formHeading + formWarning + formLabelEmail + formPlaceholderEmail + formLabelMsg + formPlaceholderMsg + formLabelPartner + formPlaceholderPartner + formSelectOptions + formLabelButton + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/footerQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/footerQuery.js new file mode 100644 index 0000000..3e06f61 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/footerQuery.js @@ -0,0 +1,9 @@ +query MyQuery { + footer { + aboutLinks + communityLinks + connectLinks + developerLinks + productsLinks + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/headerQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/headerQuery.js new file mode 100644 index 0000000..08d8a8b --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/headerQuery.js @@ -0,0 +1,5 @@ +query MyQuery { + header { + navMenu + } +} diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/homePageAllTestimonialsQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/homePageAllTestimonialsQuery.js new file mode 100644 index 0000000..b8a34e1 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/homePageAllTestimonialsQuery.js @@ -0,0 +1,11 @@ +query MyQuery { + allTestimonials { + id + testimonialImage { + url + } + testimonialPosition + testimonialText + testimonialUsername + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/homePageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/homePageQuery.js new file mode 100644 index 0000000..a4f292c --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/homePageQuery.js @@ -0,0 +1,32 @@ +query MyQuery { + homePage { + heroHeading + heroButton01 + heroB01Link + heroButton02 + heroB02Link + heroSlogan + benefitsDesc + benefitsHeading + benefitsS01Desc + benefitsS01Heading + benefitsS01Item01 + benefitsS01Item02 + benefitsS01Item03 + benefitsS01Item04 + benefitsS01Item05 + benefitsS01Item06 + benefitsS02Desc + benefitsS02Heading + benefitsS02Item01 + benefitsS02Item02 + benefitsS02Item03 + benefitsS02Item04 + benefitsS02Item05 + benefitsS02Item06 + benefitsButton + benefitsButtonLink + othersHeading + latestHeading + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/homePageTestimonialQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/homePageTestimonialQuery.js new file mode 100644 index 0000000..f95dc62 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/homePageTestimonialQuery.js @@ -0,0 +1,11 @@ +query MyQuery { + testimonial { + id + testimonialImage { + url + } + testimonialPosition + testimonialText + testimonialUsername + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/legalPagePrivacyPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/legalPagePrivacyPageQuery.js new file mode 100644 index 0000000..d5104b2 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/legalPagePrivacyPageQuery.js @@ -0,0 +1,9 @@ +query MyQuery { + privacyPage { + privacyContent { + value + blocks + } + _updatedAt + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/legalPageTermsPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/legalPageTermsPageQuery.js new file mode 100644 index 0000000..2992325 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/legalPageTermsPageQuery.js @@ -0,0 +1,9 @@ +query MyQuery { + termsPage { + termsContent { + value + blocks + } + _updatedAt + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/partnersPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/partnersPageQuery.js new file mode 100644 index 0000000..70b0eaf --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/partnersPageQuery.js @@ -0,0 +1,72 @@ +query MyQuery { + partnersPage { + heroHeading + heroParagraph01 + heroParagraph02 + oportunitiesDesc01 + oportunitiesDesc02 + oportunitiesDesc03 + oportunitiesDesc04 + oportunitiesDesc05 + oportunitiesHeading01 + oportunitiesHeading02 + oportunitiesHeading03 + oportunitiesHeading04 + oportunitiesHeading05 + oportunitiesImage01 { + url + } + oportunitiesImage01Light { + url + } + oportunitiesImage02 { + url + } + oportunitiesImage02Light { + url + } + oportunitiesImage03 { + url + } + oportunitiesImage03Light { + url + } + oportunitiesImage04 { + url + } + oportunitiesImage04Light { + url + } + oportunitiesImage05 { + url + } + oportunitiesImage05Light { + url + } + oportunitiesItem01Links + oportunitiesItem02Links + oportunitiesItem03Links + oportunitiesItem04Links + oportunitiesItem05Links + contactButtonLabel + contactCompanyLabel + contactCompanyPlaceholder + contactDescription + contactEmailLabel + contactEmailPlaceholder + contactFormHeading + contactFormWarning + contactHeading + contactInquiryLabel + contactInquiryOptions + contactInquiryPlaceholder + contactLegalLabel + contactLegalPlaceholder + contactMsgLabel + contactMsgPlaceholder + contactNameLabel + contactNamePlaceholder + contactRoleLabel + contactRolePlaceholder + } +} diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/pressPageAllPressReleasesQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/pressPageAllPressReleasesQuery.js new file mode 100644 index 0000000..504dfb2 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/pressPageAllPressReleasesQuery.js @@ -0,0 +1,10 @@ +query MyQuery { + allPressReleases { + image { + url + } + link + title + date + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/pressPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/pressPageQuery.js new file mode 100644 index 0000000..9379577 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/pressPageQuery.js @@ -0,0 +1,33 @@ +query MyQuery { + pressPage { + heroButtonLabel + heroButtonLink + heroHeading + heroLine + mediaHeading + mediaHeadingNumber + mediaVideo01Label + mediaVideo01Link + mediaVideo01Thumb { + url + } + mediaVideo02Label + mediaVideo02Link + mediaVideo02Thumb { + url + } + mediaVideo03Label + mediaVideo03Link + mediaVideo03Thumb { + url + } + mediaVideo04Label + mediaVideo04Link + mediaVideo04Thumb { + url + } + pressHeading + pressHeadingNumber + featuredHeading + } +} diff --git a/json/_datocms_migration_refrence/__apiExplorerQueries/productsPageQuery.js b/json/_datocms_migration_refrence/__apiExplorerQueries/productsPageQuery.js new file mode 100644 index 0000000..f8115f2 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueries/productsPageQuery.js @@ -0,0 +1,97 @@ +query MyQuery { + productsPage { + appDescription + appHeading + appImg { + url + } + appImgLight { + url + } + appMobileImg { + url + } + appMobileImgLight { + url + } + heroDescription + heroHeading + heroItem01 + heroItem02 + heroItem03 + heroItem04 + heroItem05 + heroS01Desc + heroS01Heading + heroS02Desc + heroS02Heading + networkDesc + networkHeading + networkImg { + url + } + networkImgLight { + url + } + networkListItem01 + networkListItem02 + networkListItem03 + networkMobileImg { + url + } + networkMobileImgLight { + url + } + stackDescription + stackHeading + stackImage { + url + } + stackImageLight { + url + } + stackListData + stackSvgImg { + url + } + stackSvgImgLight { + url + } + tokenDesc + tokenHeading + tokenImg { + url + } + tokenImgLight { + url + } + tokenItem01 + tokenItem02 + tokenItem03 + tokenMobileImg { + url + } + tokenMobileImgLight { + url + } + watchersHeading + watchersImage { + url + } + watchersImageLight { + url + } + watchersItem01 + watchersItem02 + watchersItem03 + watchersListHead + watchersMobileImage { + url + } + watchersMobileImageLight { + url + } + watchersP01 + watchersP02 + } +} diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/_allBlogPosts.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/_allBlogPosts.json new file mode 100644 index 0000000..f2fc483 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/_allBlogPosts.json @@ -0,0 +1,11230 @@ +{ + "data": { + "allBlogPosts": [ + { + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "category": [ + { + "slug": "insights", + "title": "Insights", + "id": "6311819" + }, + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic cofounder Rick Dudley appeared on a special livestream of The Interop with host Sebastien Couture to discuss the Laconic Stack, the blockchain data problems that Laconic solves, Laconic’s novel governance structure, and how Laconic can index and verify data faster, more efficiently, and at lower cost. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Below is a distilled transcript of Rick’s responses during the discussion." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Future is App Chains" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think there will be millions of chains, and we'll be using a combination of rollups and mesh–not straight linear L1, L2, L3, but also meshes of rollups and attestations publishing bridges, etc. And although we may have millions of chains, we won't have millions of massive chains. A large chain may have 100 members, and there may be one or two chains out there with 4,000 validators. But in the world, you only need a few of those." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think everything becomes an app chain. I think mainnet Ethereum ultimately becomes an app chain and the application is settling rollups–very similar to Cosmos Hub, frankly. Polkadot, Ethereum 2.0, Cosmos Hub are all actually very similar in terms of the endgame state in the final thesis. And I don't think that there will necessarily be a winner per se. I think they will have curious different properties. " + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Why Laconic?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The ultimate goal of Laconic is to get all of the data that a user is concerned about in the hands of that user. Not in a cloud-hosted environment, not in Microsoft, not in AWS, but in users’ actual custody. And to enable them to do all the verification themselves." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Right now, it’s very difficult to extract parts of data from the Ethereum Mainnet that are relevant for Dapp needs. It’s almost impossible to synchronize a Geth node in a reasonable amount of time." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are multiple light client protocols that have come around to help alleviate this problem but they still don't go all the way. The Laconic Network goes the whole way. It goes from source code, to what is in the user's eyeballs with everything being verifiable. If you see a message on Laconic that came to you through the Laconic Network, you could say, \"I want to know which blockchain or blockchains this came from. I want to know what code generated this result. I want to know who wrote that code.” We provide all of that in the Laconic Network." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Three Major Components of Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is the Laconic LLC itself, which is in the Cayman Islands. There is the Laconic Stack, which is the standalone software that anyone can run today to generate this data and the evidence that they need. And then there's the Laconic Network, which facilitates the buying and selling of data. It facilitates running these services, discovering the services, paying for services, and then making sure all of that is verifiable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Those three components are an evolution. We've iterated on the Stack many times over at this point. MakerDAO is still using an early version of that stack to this day last I checked, which was recently. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you were an intrepid developer, you could go into the Stack Orchestrator code and run that yourself and put that into production yourself right now. But the problem with that is it's very expensive to generate this evidence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s computationally very expensive, and specifically, disk I/O operations are very expensive activities to do. So as a Dapp developer, when you have very few users, you can run this reasonably on the laptop. But as your app grows, or if you're wanting to see all of the Uniswap V3 pool data, then a laptop's not going to be able to process that in a timely manner necessarily. I mean, laptops are pretty powerful so some of them can, but maybe not all of them. And at that point, you need hardware. And when you need hardware, you then have this problem of, \"Okay, well am I going to buy hardware and rack it in a data center?\" That's probably not a viable answer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Am I going to go to AWS? Well, AWS is centralized, there are all sorts of problems. There's censorship for instance. AWS may choose to comply with a law that I'm not legally obligated to comply with. We've seen this issue with Alchemy and Infura, and these solutions comply with the laws in their jurisdiction, but the Dapp developer is in a different jurisdiction. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So then you end up with this situation; \"Okay, well if I want to have multiple service providers actually serving this data to users, they need to be in multiple jurisdictions.\" And that's what Laconic LLC solves. It's a Cayman Island LLC. We have members in different jurisdictions and those members will contract with the end users and comply with those laws in that way." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and Cosmos" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic team members were also core contributors to Cosmos SDK–we did a bunch of work on the Cosmos SDK. The data structures in Ethereum and the data structures in Cosmos and many other blockchains were designed to facilitate consensus, not to facilitate reading the data back out. And so in those architectures, there's utility in taking the techniques that we've applied to Ethereum and applying them to those other chains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is a value and utility to taking those techniques and applying them to the Cosmos SDK chains. Osmosis is an example of where it would be useful. For example, you can't have a block explorer that works across Cosmos Hub upgrades. No one's ever bothered to build one that works that way. If you built the block explorer on top of Laconic instead of directly on top of the chain, you would actually be able to provide that continuity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Every time a Cosmos chain upgrades, they regenesis and restart the chain. When you start that new genesis, people–just as a matter of convenience–don't preserve that data. You don't have a way of representing the irregular state change that happens during the upgrade. Whereas in the Laconic system, we have a means of doing all those things. We can link any two arbitrary chains together and we have a means of representing these arbitrary state changes. We can provide that continuity as a service." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentive Alignment " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because we're IPLD based, we actually can relatively easily take our archive and push it into Filecoin, where there can then be this clear monetization strategy for storing the data. Because we monetize the transmission of the data, which is a much easier problem to solve than the verifiable storage of Filecoin, we're providing an incentive for why someone would do that. Think about it. There are different incentives throughout the process. There's an incentive for including the transaction. That incentive is very clear, but there's not really any incentive in any blockchain I'm aware of for why I should then send that data. Why should I satisfy a read from a user? A user asks for a read, and why do I care?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That's what Laconic is trying to solve–we're incentivizing the reading of that data. And by incentivizing the reading of that data, that's step number two. Now we can talk about the incentives of step number three, which is a long-term persistent storage of that data. Because if you think about just having the incentives of just Filecoin and just Ethereum, you have this gap in the middle. Why do I take the Ethereum data and transform that and publish it to Filecoin? There's not really an incentive for me to do that." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whereas with Laconic, there starts to become more of an incentive to do that because I need to support my own read infrastructure. People will come to you and know to come to a single place to get their historic reads as well as their more recent reads. And so you’ll be incentivized to charge them. There will already be an ecosystem in place where people are accustomed to paying for data. And when they want to pay for old data or new data, they'll come to the same place, buy that data, and that will incentivize archival storage. Right now we don't have a very good model for why archival storage persists. And it is a real mechanism design issue actually." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "InterPlanetary Linked Data (IPLD) is the core of our system. The first thing we do is take the Ethereum data, which could be any blockchain or any hash linked data structure, and we convert that into an IPLD object. We then index it in that context. We’re storing the RLP encoded bytes, but we are also storing the CID (Content ID), the multi-format address of that object. That's how we're able to generate evidence." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On Ethereum, you have transaction receipts and you have the event messages. When you have an event message on Ethereum, the event message does not prove all the way back up to the root. So when you have a set of events, which is what The Graph consumes, the way that you prove that event is correct is that you find the block that that event was in, and then you rerun that whole block and at the end of it you see if you have the same event that you started with. Whereas, if I have an account balance on Ethereum, I have a block number and then I can get a proof. So I don't have to recompute the whole block to figure out the account balance in that block. I just get the proof from the Ethereum client about that account balance at that block, and I can present that proof and the balance to the user using eth_getProof." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But the actual logs in Ethereum are not provable in this way. This is why The Graph isn't provable and there are a lot of consequences from this. But because we use IPLD, we can create those hash links. Where the link was missing in the original Ethereum protocol, we can augment that protocol and generate a proof using the Ethereum data and our additional links, which are relatively easily. It's not some weird, crazy different format. It's this format that is very similar to the existing Ethereum formats, that prove that this log actually came from this block." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Member Validators" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic L2 has seven Founding Members right now. These seven Members validate, ingest the blocks, and make commitments to the state of those blocks. They then share that information with a paying customer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are plans to increase the validator set. From a customer perspective, if our customer is a Dapp developer and they're saying, “right now I have to use Infura, Alchemy, Blocknative to assert that my data is correct because if one of them goes down for whatever reason, that's three right there.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That sounds like a pain in the ass. With Laconic, you integrate one protocol and you get seven Member Validators instead of three, and you get an assertion from us that you can verify yourself that we're actually physically located in different places. Alchemy and Infura both run in AWS, I presume. If AWS goes down, you just lost two out of your three, if not all three out of your three in that case. Seven is a low number, but seven is incredibly high compared to what people have right now, or they think they have four and they have one, whereas we're positively asserting that you have seven." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "RPC Services and Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the path to building Watchers, we realized we had to build extremely performant RPC endpoints, and we had to build out a deployment system. We realized that that was actually what people wanted to buy from us. Most Dapps don’t want to bother with Watchers right now. What they want to see is this immediate savings on the RPC endpoint side. From there, oncet our foot is in the door, we can say, \"Well, we can give you even more savings. You are using that RPC endpoint to build your own indexer. We have a whole library of tools to build indexers that will auto-generate indexers for you. And we have a marketplace where you can go to get other people to run that indexer for you when you don't want to scale it.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Currently, RPC endpoints are subsidized by VCs. Dapp developers are never experiencing the true cost of running an indexing service or running an RPC endpoint. They're not exposed to that in a free market way. There's this actor, this venture capitalist, who is going in and giving away free samples at a massive scale. The challenge for us is in how we compete with that? There's also a challenge in that our customers are depending on this centralization service and don't realize it. " + } + ] + } + ] + } + } + }, + "date": "2023-02-09", + "featured": false, + "id": "64080923", + "image": { + "url": "https://www.datocms-assets.com/66113/1675901476-laconic-seb-blog.png" + }, + "slug": "rick-dudley-on-the-interop", + "title": "Rick Dudley Discusses Laconic Network on The Interop" + }, + { + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "category": [ + { + "slug": "developers", + "title": "Developers", + "id": "6311820" + }, + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In " + }, + { + "url": "https://www.laconic.com/blog/intro-to-the-laconic-stack", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the last blog post" + } + ] + }, + { + "type": "span", + "value": ", we introduced all the main components of the Laconic Stack. The first point of entry for any developer wishing to use Laconic is " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "span", + "value": ". It allows you to stand up a local fixturenet for testing purposes. Integrated directly into Stack Orchestrator are several \"stacks\" which work out of the box. Today, we'll be going over the ERC20 \"stack\" to provide an overview of some key components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You will accomplish the following:\n" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "stand up the core stack using Stack Orchestrator" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 token" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 Watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "send tokens to and from your local account to a new account on Metamask" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "use GraphQL to query the watcher for information about the token and accounts" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This tutorial assumes you are on a local machine (Mac or Linux). Trying it in the cloud requires additional configurations (e.g., opening ports) not covered here." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Pre-requisites" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "python3" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://www.python.org/downloads/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/get-docker/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker-compose" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/compose/install/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MetaMask " + }, + { + "url": "https://metamask.io/download/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "span", + "value": " in the supported browser of your choice." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If using a fresh Ubuntu Digital Ocean droplet, check out " + }, + { + "url": "https://github.com/LaconicNetwork/Laconic-Documentation/blob/staging/scripts/install-laconic-stack.sh", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this script" + } + ] + }, + { + "type": "span", + "value": " for a quick setup." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WARNING" + }, + { + "type": "span", + "value": ": if installing docker-compose via package manager (as opposed to Docker Desktop), you must install the plugin, e.g., on Linux:" + } + ] + }, + { + "code": "mkdir -p ~/.docker/cli-plugins\ncurl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose\nchmod +x ~/.docker/cli-plugins/docker-compose", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, install the latest release of Stack Orchestrator" + } + ] + }, + { + "code": "curl -L -o laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Give it permission:" + } + ] + }, + { + "code": "chmod +x laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Verify operation:" + } + ] + }, + { + "code": "./laconic-so \nUsage: python -m laconic-so [OPTIONS] COMMAND [ARGS]...\n\n Laconic Stack Orchestrator\n\nOptions:\n --stack TEXT specify a stack to build/deploy\n --quiet\n --verbose\n --dry-run\n --local-stack\n --debug\n --continue-on-error\n -h, --help Show this message and exit.\n\nCommands:\n build-containers build the set of containers required for a complete...\n build-npms build the set of npm packages required for a...\n deploy-system deploy a stack\n setup-repositories git clone the set of repositories required to build...\n version print tool version", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For a more permanent setup, move the binary to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "~/bin" + }, + { + "type": "span", + "value": "` and add it your `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PATH" + }, + { + "type": "span", + "value": "`." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so" + }, + { + "type": "span", + "value": "` CLI tool makes it easy to experiment with various components of the stack. It allows you to quickly and seamlessly experiment with watchers. Because it uses docker/docker-compose, several commands in this tutorial will leverage the ability to execute commands directly in the containers. This, for example, means that `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "yarn" + }, + { + "type": "span", + "value": "` doesn't need to be installed on your local machine." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Setup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Use the stack orchestrator to pull the core repositories:" + } + ] + }, + { + "code": "./laconic-so --stack erc20 setup-repositories", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see something like:" + } + ] + }, + { + "code": "Dev Root is: /root/cerc\nChecking: /root/cerc/go-ethereum: Needs to be fetched\n100%|####################################################################################################| 71.6k/71.6k [00:23<00:00, 3.10kB/s]\nChecking: /root/cerc/ipld-eth-db: Needs to be fetched\n100%|##########################################################################################################| 595/595 [00:00<00:00, 991B/s]\nChecking: /root/cerc/ipld-eth-server: Needs to be fetched\n100%|####################################################################################################| 25.5k/25.5k [00:06<00:00, 3.82kB/s]\nChecking: /root/cerc/watcher-ts: Needs to be fetched\n100%|####################################################################################################| 8.79k/8.79k [00:01<00:00, 4.49kB/s]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we'll build the docker images for each repo we just fetched." + } + ] + }, + { + "code": "./laconic-so --stack erc20 build-containers ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This process will take 10-15 minutes, go make a pot of coffee. The output will give you an idea of what's going on. Eventually, you'll see:" + } + ] + }, + { + "code": "Successfully built 77c75d57ad66\nSuccessfully tagged cerc/watcher-erc20:local", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, let's deploy this stack:" + } + ] + }, + { + "code": "./laconic-so --stack erc20deploy-system up", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The output will looks like this (ignore the warnings):" + } + ] + }, + { + "code": "WARN[0000] The \"eth_proxy_on_error\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_forward_eth_calls\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_http_path\" variable is not set. Defaulting to a blank string. \n[+] Running 23/23\n ⠿ ipld-eth-db Pulled 18.4s\n ⠿ 213ec9aee27d Already exists 0.0s\n ⠿ 85c3ef7cf9a6 Pull complete 0.7s\n ⠿ ac29cc04759a Pull complete 0.9s\n ⠿ 2a37e244d86b Pull complete 13.5s\n ⠿ 36d7202aa1cf Pull complete 13.8s\n ⠿ 3acdddb9790a Pull complete 13.9s\n ⠿ 9a938759f2bf Pull complete 14.1s\n ⠿ 5d65a6241248 Pull complete 14.2s\n ⠿ cee6999f074e Pull complete 14.4s\n ⠿ 20b12472cb73 Pull complete 14.8s\n ⠿ 65467bb36f5f Pull complete 16.2s\n ⠿ fe6050bae51d Pull complete 17.4s\n ⠿ 519306d43b4a Pull complete 17.9s\n ⠿ erc20-watcher-db Pulled 15.0s\n ⠿ 8921db27df28 Already exists 0.0s\n ⠿ eb286326f602 Pull complete 0.3s\n ⠿ 63139c77dd7e Pull complete 0.5s\n ⠿ 17baeacd3984 Pull complete 13.5s\n ⠿ 5f08b9782916 Pull complete 13.8s\n ⠿ a836be7ad658 Pull complete 14.0s\n ⠿ 1966853affaf Pull complete 14.2s\n ⠿ 4dc6d2c8dede Pull complete 14.4s\n[+] Running 8/8\n ⠿ Network laconic-30c27a9be20b005274dfc23fd7e90256_default Created 0.1s\n ⠿ Volume \"laconic-30c27a9be20b005274dfc23fd7e90256_erc20_watcher_db_data\" Created 0.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1 Healthy 33.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1 Healthy 34.8s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1 Started 32.7s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1 Started 33.1s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1 Healthy 53.5s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1 Started 54.3s", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's take stock of what just happened, we:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "cloned a bunch of repos: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so setup-repositories" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "built all of their docker images: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so build-containers" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed these images as services that know about each other: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so deploy-system up" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Take a look at all the running docker containers:" + } + ] + }, + { + "code": "docker ps", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You should see 6 containers:" + } + ] + }, + { + "code": "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n605ccf0e4461 cerc/watcher-erc20:local \"docker-entrypoint.s…\" 6 minutes ago Up 5 minutes (unhealthy) 0.0.0.0:3002->3001/tcp, 0.0.0.0:9002->9001/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1\n0a00a3a1bcd6 cerc/ipld-eth-db:local \"/app/startup_script…\" 6 minutes ago Up 5 minutes laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1\nf4aece866e48 cerc/ipld-eth-server:local \"/app/entrypoint.sh\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8081-8082->8081-8082/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1\nebe0dc8cd2b4 cerc/go-ethereum-foundry:local \"./start-private-net…\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8545-8546->8545-8546/tcp laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1\n72263d100b8c postgres:14-alpine \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 0.0.0.0:15433->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1\nd2effc54624c timescale/timescaledb:2.8.1-pg14 \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 127.0.0.1:8077->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, via the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "watcher-erc20" + }, + { + "type": "span", + "value": "` container, the " + }, + { + "url": "https://graphql.org/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "span", + "value": " playground is enabled on " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and you should check that it is there:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great so now we have the core stack up and running, let's deploy an ERC20 token." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, we need the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER ID" + }, + { + "type": "span", + "value": "` of the ERC20 watcher:" + } + ] + }, + { + "code": "docker ps | grep \"watcher-erc20\"", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Using the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ID" + }, + { + "type": "span", + "value": "` from the example above, we'll export the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER_ID" + }, + { + "type": "span", + "value": "` for use throughout the rest of the tutorial:" + } + ] + }, + { + "code": "export CONTAINER_ID=605ccf0e4461", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we can deploy an ERC20 token (currency symbol GLD):" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:deploy:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and your output should look like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-deploy\nDownloading compiler 0.8.0\nCompiled 5 Solidity files successfully\nGLD Token deployed to: 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\nDeployed at block: 9087 0x4dc63b4b2695b644d7d390d70c9de0232399ea4d54b8c75911eee14c13f9ceaf\nDone in 157.39s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, now that we've deployed the GLD token, you'll want to export its address for later use:" + } + ] + }, + { + "code": "export TOKEN_ADDRESS=0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Get your primary account address with:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn account:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and the following output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker account\n0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nDone in 21.63s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "export that address to your shell:" + } + ] + }, + { + "code": "export PRIMARY_ADDRESS=0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To get the latest block hash at any time, run:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "for an output like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker block-latest\nBlock Number: 12783\nBlock Hash: 0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\nDone in 21.44s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll configure MetaMask." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "MetaMask" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Open MetaMask in your browser:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Add Network\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Scroll to the bottow and click \"Add Network Manually\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Put in this information:" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you see the error above \"This URL is currently used by the Localhost 8545 Network\", change `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "localhost" + }, + { + "type": "span", + "value": "` to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "127.0.0.1" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We will come back to MetaMask later and complete this process; for now, copy your new address" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and export it for later:" + } + ] + }, + { + "code": "export RECIPIENT_ADDRESS=0x988a070c97D33a9Dfcc134df5628b77e8B5214ad", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Head on over to " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and paste the following (but with your variables):" + } + ] + }, + { + "code": "query {\n name(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n symbol(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n totalSupply(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "then click \"Run\" and you'll see a response like this:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"name\": {\n \"value\": \"Gold\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzavwb52aq6smf6movgcimvuoggp3cifayb2vyidg3ar546pwtb3dea\\\",\\\"ipldBlock\\\":\\\"0xf843a032575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85ba1a0476f6c6400000000000000000000000000000000000000000000000000000008\\\"}}}]\"\n }\n },\n \"symbol\": {\n \"value\": \"GLD\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzanp5bxcn6wqd2yptbbwo5o4rx3mhpji43yd7sfd42suq6hjuhuroq\\\",\\\"ipldBlock\\\":\\\"0xf843a03a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19ba1a0474c440000000000000000000000000000000000000000000000000000000006\\\"}}}]\"\n }\n },\n \"totalSupply\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzasfla7wzuessejihdtrxqd5lqxc57egukbbricizz2ssrltex4uvq\\\",\\\"ipldBlock\\\":\\\"0xeca0305787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace8a893635c9adc5dea00000\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here's what it'll look like in the browser:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A lot has happened thus far, so let's review; we've:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "downloaded the core repos, built their docker images, and launched a local network (all using stack orchestrator)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed an ERC20 token, added it to our MetaMask account" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "used the GraphQL playground to query the ERC20 watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "exported a handful of shell variables which are about to come in handy" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll use the playground to query account balances:" + } + ] + }, + { + "code": "query {\n fromBalanceOf: balanceOf(\n # latest block hash\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # primary account having all the balance initially\n # created by stack orchestrator\n owner: \"0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\"\n ) {\n value\n proof {\n data\n }\n }\n toBalanceOf: balanceOf(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # address copied from MetaMask, has no balance initially\n owner: \"0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "the primary address should have " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " 1000000000000000000000 and the recipient address should have 0:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzarsopkngjoijjyktfhgckq7te4dsk25gfyj653uxu4kcwqoyuykiq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5dea00000\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"0\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"\\\",\\\"ipldBlock\\\":\\\"0x\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note also that the recipient address also does not yet have a `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "cid" + }, + { + "type": "span", + "value": "` or `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ipldBlock" + }, + { + "type": "span", + "value": "`, which makes sense; this is a random account we just created and hasn't received any transactions. The network does not know about it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send it some GLD!" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:transfer:docker --token $TOKEN_ADDRESS --to $RECIPIENT_ADDRESS --amount 100000000 ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see a familiar output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-transfer --token 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550 --to 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad --amount 100000000\nNothing to compile\nTransfer Event at block: 13371 0x412dbc25599773bfe929c67882e4a001b9d1b3b8e1c60ad4a495d5306608c77a\nfrom: 0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nto: 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\nvalue: 100000000\nDone in 26.12s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now get the latest block hash:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and go back to the GraphQL playground. If you've changed nothing since the last query, update only the latest block hash and run the query again, you'll see the updated account balances:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"999999999999900000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzahg5shtf2rr7pompx7yx6r22zu4lea7ftlnbqkqcabvndsrvoljhq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5d8aa1f00\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"100000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzas6xotntgq4u3v4eui6pmtbyttgikzmu7mppknam2wrekhoynupjq\\\",\\\"ipldBlock\\\":\\\"0xe7a03305adb1a8efab310b21e03d5a9f08d8cf98815372c2c4d8068e1359b8f996bc858405f5e100\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, you've now used a watcher to see query token balances." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send some tokens from the MetaMask recipient account back to the primary account." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Recall that when adding the network to MetaMask, we used the currency symbol \"GLD\". However, this does not mean that MetaMask can auto-detect the token, therefore, we will have to manually import it:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Copy the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TOKEN_ADDRESS" + }, + { + "type": "span", + "value": "` and paste it in the popup. The two other fields should auto-complete:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Import Token\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and now you'll see your balance. Ignore the GLD token from earlier." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, send some tokens back to the primary address using MetaMask:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Make the gas price `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "0" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Grab the latest block hash (again) and fire off the GraphQL query for account balances to see the change." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Voila! You've successfully stood up the core Laconic stack, deployed an ERC20 token, and queried account balances." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cleanup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tear down your docker containers with:" + } + ] + }, + { + "code": "./laconic-so deploy-system --stack erc20 down", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Next steps" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Try out the " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator/tree/main/app/data/stacks/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "ERC721 demo" + } + ] + } + ] + } + ] + } + } + }, + "date": "2023-01-31", + "featured": false, + "id": "64023526", + "image": { + "url": "https://www.datocms-assets.com/66113/1675106863-laconic_clippy_watcher_02.png" + }, + "slug": "erc20-watcher-demo", + "title": "ERC20 Watcher Demo" + }, + { + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "category": [ + { + "slug": "developers", + "title": "Developers", + "id": "6311820" + } + ], + "content": { + "blocks": [ + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here’s the main problem: reading data from the Ethereum blockchains is either cheap and sloppy or expensive and correct. As a result, Dapp developers have come to rely on inexpensive centralized services that do not provide evidence to verify the correctness of the data they are serving to Dapps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only is it expensive to get verifiable data but it can also be challenging to parse out the subset of data you really need. In the early days of SQL, you had to be proficient at the command line in order to use the product, and so use was limited to those that had that specialized capability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Eventually, GUIs were built such that anyone with basic computer skills could use drop-down menus and create database schemas without writing a single line of code. Web3 is still in the early days of SQL, it is not easy onboarding new users and developers who are otherwise quite capable with the latest Web2 technologies.\n\nRight now, there’s all this data on Ethereum and as a Dapp developer, you only want a tiny fraction of it. But, to verify that fraction, you have to (among several other things) maintain an archive node - this is prohibitively expensive for the majority of developers. To solve this problem, centralized services such as (Infura, The Graph, and Alchemy) have popped up and currently account for the majority (if not most) of Dapp queries to Ethereum." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic was created to address these and several other problems in the blockchain ecosystem. Not only does Laconic make it easy to get verifiable data - quickly and cheaply - it also provides a framework for data transformation and aggregation that are difficult or impossible to do in other systems.\n\nArchitecting a solution to this requires many moving pieces; these have been developed by core Ethereum & Cosmos contributors over the past 5 years. In this post, we will walk you through the various components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are three different ways to participate in the Laconic Network: Member Validators, Service Providers, and Dapp Developers. To describe the responsibilities and benefits of each role, we must first start grounded in the technicalities of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let’s take a look at the following core stack diagram:" + } + ] + }, + { + "item": "63992474", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note: this diagram intentionally leaves out several repositories (e.g., codecs, utilities, rpc shims). This is done for simplicity reasons and anyone diving deep into the stack will discover them.\n\nThe two repositories at the top are also the main entry points for most developers. `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` is a command-line tool for, well, orchestrating the stack. It uses docker-compose to deploy a specified collection of networked docker containers, thereby eliminating the need to set up a variety of services independently. Every user of the Laconic Stack will at some point - if not regularly - use the stack orchestrator." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watchers-ts" + } + ] + }, + { + "type": "span", + "value": "` repo contains the publically available Watchers and the code to generate them. Watchers are TypeScript that is generated from one or more Solidity smart contracts. Dapp Developers can participate in the Laconic Network by either 1) writing a custom watcher for their Dapp or 2) writing a generally useful watcher and publishing it to the Laconic Registry, thus earning a fee every time it is used. We’ll come back to Watchers later." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Down at the bottom left is the `" + }, + { + "url": "https://github.com/cerc-io/laconicd", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconicd" + } + ] + }, + { + "type": "span", + "value": "` repository and it is indeed the “bottom” of the stack. It is built from the Cosmos SDK and has custom modules specific to operating the Laconic Network (e.g., fork of Ethermint/Evmos, auction, nameservice). It is likely that in the future there will be a public testnet, however, because the Laconic Network is a permissioned validator set, only Member Validators that have officially joined the Laconic Network will be included in the mainnet. Just because the validator set is permissioned does not prohibit anyone from running a full node and Service Providers or others may choose to do so for a variety of reasons." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/laconic-sdk", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-sdk" + } + ] + }, + { + "type": "span", + "value": "` is a library for facilitating talking to `laconicd`. Both the `" + }, + { + "url": "https://github.com/cerc-io/laconic-registry-cli", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-registry-cli" + } + ] + }, + { + "type": "span", + "value": "` and the `" + }, + { + "url": "https://github.com/cerc-io/laconic-console", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-console" + } + ] + }, + { + "type": "span", + "value": "` use it. While `laconic-registry-cli` is a command-line tool for doing so, the `laconic-console` is a user interface for writing and reading records on the Laconic Network. These general-purpose tools are useful for a wide variety of use cases across the stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A key goal of the Laconic Network is to provide accurate, verifiable data from the Ethereum blockchain. Comparisons to currently available solutions are for another post, however, no service currently exists to provide inexpensive evidence that the data being served is correct. The Laconic solution (one of) to this is in something called “statediffing”, a part of the stack run by Member Validators and, likely by Service Providers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It starts with a maintained fork of `" + }, + { + "url": "https://github.com/cerc-io/go-ethereum", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "go-ethereum (geth)" + } + ] + }, + { + "type": "span", + "value": "` that has an added real-time state-diffing service. Statediffing gives a clear picture of the state between any given blockheights. This allows Laconic to minimize the amount of computation required for providing proofs. Three additional “helper” services perform different tasks required to get a full picture of the state as required by an application. Together, these comprise the Full Index Node (FIN)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-service" + } + ] + }, + { + "type": "span", + "value": "` provides historical state data, while the `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-fill-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-fill-service" + } + ] + }, + { + "type": "span", + "value": "` uses the historical state data to fill statediff gaps as required. Finally, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-state-snapshot", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-state-snapshot" + } + ] + }, + { + "type": "span", + "value": "` loads a complete state at a certain blockheight, which helps to bootstrap the system. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nThere is more to be written about statediffing, however, what’s important to note here is that each service is writing independently to `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-db", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-db" + } + ] + }, + { + "type": "span", + "value": "`. The latter serves as a bucket for state data that has been indexed in IPLD. Rather than querying this database directly, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-server", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-server" + } + ] + }, + { + "type": "span", + "value": "` provides an API layer for Watchers to easily query relevant pieces of data from the Ethereum state. Additionally, `ipld-eth-server` recapitulates the native Ethereum JSON RPC interfaces on top of the `ipld-eth-db` database." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "And so we’ve come full circle back to the Watchers. As previously mentioned, they are generated from one or more Solidity smart contracts and configured to query specific pieces of data relevant to a Dapp. Watchers make it easy to query the data you need from Ethereum " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "and" + }, + { + "type": "span", + "value": " - along the way - get evidence in order to generate proofs that your data is correct. This is in contrast to currently available solutions for Dapp developers, who must currently rely on centralized providers that don’t provide evidence to generate proofs." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Web3 is (still!) in its early days, and like the early days of SQL, Dapp developers need advanced knowledge of complex data structures to build their Dapp. Watchers simplify this by exposing a GraphQL endpoint, a solution familiar to an order of magnitude more developers than querying the Ethereum blockchain directly." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is building a suite of tools to address core problems in Web3. Today, we’ve provided an overview of the main components of the Laconic Stack. Developers interested in Laconic should start with `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` to get a sense of running different parts of the stack, then check out `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watcher-ts" + } + ] + }, + { + "type": "span", + "value": "` to experiment with different watchers and progress to making their own." + } + ] + } + ] + } + } + }, + "date": "2023-01-18", + "featured": false, + "id": "63992475", + "image": { + "url": "https://www.datocms-assets.com/66113/1673986992-laconic_clippy_grid2.png" + }, + "slug": "intro-to-the-laconic-stack", + "title": "Intro to the Laconic Stack" + }, + { + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "category": [ + { + "slug": "insights", + "title": "Insights", + "id": "6311819" + } + ], + "content": { + "blocks": [ + {}, + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So much has changed in the last year due in large part to a seemingly unpredictable sequence of epic market failures. Despite this, we’re still here trying to predict the unpredictable. As the dust settles from a cascade of collapses including FTX, Terra, 3AC, and many others, the direction crypto must take to become viable is now clearer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We find ourselves in a strange position. We built protocols that actually work under immense market pressure, showing some of the promise of decentralized systems. However, that pressure came from deeply irresponsible market manipulation and behavior. How do we make sense of the fallout, and do better. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Blockchain Regulation: it’s coming" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, this space is about to get regulated. The " + }, + { + "url": "https://www.pillsburylaw.com/en/news-and-insights/digital-commodities-consumer-protection-act-digital-assets.html", + "type": "link", + "children": [ + { + "type": "span", + "value": "Digital Commodities Consumer Protection Act" + } + ] + }, + { + "type": "span", + "value": " (DCCPA) looms in the wake of FTX’s demise and Sam Bankman Fried’s increasingly radioactive support of the bill. The DCCPA aims to give the CFTC primary jurisdiction over crypto exchanges. This would establish the commission as the authority on spot, margined and leveraged digital commodity trades. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The bill, however, does not outline processes for classifying assets as commodities and securities. This is relevant because the Securities and Exchange Commission (SEC), which under Gary Gensler has increasingly strived for more oversight, currently qualifies most crypto, aside from Bitcoin, as a security. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In Europe the new Markets in Crypto Assets Regulation (MiCA) aims to regulate dollar-pegged stablecoins, controlling transaction count and volume limits which, " + }, + { + "url": "https://www.reuters.com/technology/eu-crypto-rules-set-cap-dollar-pegged-stablecoins-2022-10-07/#:~:text=LONDON%2C%20Oct%207%20(Reuters),competitiveness%2C%20industry%20representatives%20have%20said.", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "according to Reuters" + } + ] + }, + { + "type": "span", + "value": ", are already exceeded by the three largest stablecoins, Binance USD, USD Coin & Tether. Stablecoin volume has increased dramatically over the last several years and a cap or ban could hinder the crypto ecosystem overall. Crypto regulation and the conversations around asset classifications, regulatory purview, and stablecoin usage will be defining battlegrounds in 2023. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Decentralization: yes it matters" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Increasing regulatory pressure will force crypto projects to re-focus on achieving actual decentralization; not just to avoid legal consequences but also to empower more equitable and effective on-chain governance. Although there is no official guidance from the SEC, commissioner " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Hester Pierce’s Safe Harbor Proposa" + }, + { + "type": "span", + "value": "l has served as a guiding light for those interested. The proposal suggests networks must achieve sufficient decentralization within three years, meaning networks should not be controlled by a single individual or group of individuals. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tokens must also be used to exchange value pertaining to the network. The token should be sold for facilitating access to, participation on, or development of the network. Essentially, if it’s a decentralized network, validators/investors/teams cannot control majority stake. If the network has a token, then the token needs to be used for something " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "actually" + }, + { + "type": "span", + "value": " intrinsic to network operations–it can’t just be a fundraising vehicle." + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "On-Chain Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve touched on a number of external forces impacting networks but what about internal forces? On-chain governance is rapidly evolving. Usage of proof of stake (PoS) consensus has grown over the last several years in part due to a dramatic reduction in power consumption, and also because of increased flexibility to affect significant changes to network parameters. With voting power relative to stake, stake concentrated in the hands of a few entities can present unique and potentially insurmountable challenges to achieving sufficient decentralization." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Cosmos ecosystem is a particularly fascinating testing ground for on-chain governance. From the controversial " + }, + { + "url": "https://www.mintscan.io/juno/proposals/16", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Juno Prop 16" + } + ] + }, + { + "type": "span", + "value": " which resulted in on-chain confiscation of user funds, to the equally controversial " + }, + { + "url": "https://www.mintscan.io/cosmos/proposals/82", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Cosmos Prop 82" + } + ] + }, + { + "type": "span", + "value": " which put forward an entirely new paradigm for Cosmos Hub utility and ATOM value capture, the Cosmos ecosystem has been trailblazing governance processes and implementation. " + } + ] + }, + { + "item": "63885761", + "type": "block" + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What’s at Stake?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the main issues we’re seeing now is the disproportionate concentration of stake. In Cosmos for instance, the top 5 out of 150 " + }, + { + "url": "https://www.mintscan.io/juno/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Juno validators" + } + ] + }, + { + "type": "span", + "value": " control 25% of the network. The top 5 out of 175 " + }, + { + "url": "https://www.mintscan.io/cosmos/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Cosmos validators" + } + ] + }, + { + "type": "span", + "value": " control 25% as well. The top 5 out of 100 " + }, + { + "url": "https://www.mintscan.io/akash/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Akash validators" + } + ] + }, + { + "type": "span", + "value": " control 37%. " + } + ] + }, + { + "item": "63885758", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In Ethereum, " + }, + { + "url": "https://cointelegraph.com/news/64-of-staked-eth-controlled-by-five-entities-nansen", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "according to Nansen" + } + ] + }, + { + "type": "span", + "value": ", there are 5 entities that control 64% of staked Ether. Although only 11% of circulating ETH is staked, among 426,000 validators, three major cryptocurrency exchanges account for 30% of the total staked ETH, with Lido DAO accounting for 31%. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If the top 4-5 Validators control 1/4 of the stake and, at times more, of a network, is the network really decentralized? If an exchange like Binance or Huobi can buy up large volumes of tokens, enter the top validator slots, and through governance, make substantive changes to the network for their own benefit, is that network truly decentralized? There are valid arguments on both sides. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There’s an argument that these networks are permissionless and any entity can engage in governance by purchasing tokens and staking them, so in theory, they are decentralized. In practice, power is often concentrated in the hands of a few entities with a majority of the tokens. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The current Cosmos governance landscape is also fascinating because many pivotal governance mechanisms and network changes are being deliberated simultaneously. Should there be a constitution? Should there be larger deposit requirements for proposals? Should ATOM issuance be changed to fund network growth initiatives–completely repurposing the network in the process? Cosmos will continue to innovate on-chain governance. As Cosmos gains more visibility, developers will adapt components of Cosmos governance for their purposes. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Novel Solutions to Crypto’s Most Existential Problems" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As we look ahead to 2023, Laconic is well positioned to address these issues. Laconic Network leverages Cosmos technology and is run by an LLC that is designed to comply with the laws within its jurisdiction and to allow each member to comply with the laws within their jurisdiction. Geographic distribution of member validators strengthens the network's resilience to nation specific sanctions. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Additionally each member validator is given one vote and all votes are equally weighted. This eliminates uneven distribution of voting power typically seen in delegated proof of stake networks. Laconic Network also makes it clear that Laconic Network Token (LNT) is treated as a loyalty point and for prepayment of services, and is not a security or currency. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For more on the Laconic Network Governance model and how it addresses the fundamental issues around asset classification, token usage, PoS networks and regulation, you can check out “" + }, + { + "url": "https://www.laconic.com/blog/laconic-governance-model", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "A New Governance Model for the New Web" + } + ] + }, + { + "type": "span", + "value": ".” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Crypto isn’t going anywhere, but we will need to build safer and more decentralized systems, and work within a more aggressive regulatory environment. " + } + ] + } + ] + } + } + }, + "date": "2023-01-05", + "featured": false, + "id": "63853186", + "image": { + "url": "https://www.datocms-assets.com/66113/1672273151-screen-shot-2022-12-28-at-4-19-05-pm.png" + }, + "slug": "trends", + "title": "A Look Ahead at Crypto in 2023" + }, + { + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "category": [ + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network was founded to develop crucial infrastructure for current and future generations of blockchains and Web3 applications. That's an ambitious mission, and one that demands significant technological innovation. Perhaps even more innovative, however, is the company's governance model—a unique organizational " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "structure that, along with the Laconic token, underlies" + }, + { + "type": "span", + "value": " a scalable and sustainable project with the power to address the many operational, regulatory, and ideological challenges of Web3. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Operational challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network’s governance model addresses four primary operational challenges:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Network funding. The Laconic governance model funds initial network development and growth. But unlike most venture capital-funded businesses, Laconic avoids passive investment, instead requiring all founding members to make significant contributions of capital, engineering expertise, or both." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Network growth. To ensure timely continued growth, the Laconic Network must scale along with demand for Web3 data services—necessitating clear procedures for adding and removing Members and Service Providers." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Hardware operations. Laconic indexing and services are computationally expensive, requiring significant hardware and infrastructure investment. Laconic is creating a decentralized infrastructure network built for scalability and flexibility, and designed to deliver data to consumers at the highest possible service level." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Corporate governance. Clear and fair governance is essential to any decentralized project. The Laconic limited liability company agreement (LLCA) clearly specifies how stakeholders including Members, Service Providers, and data customers may participate in the governance process; proposal submission and voting processes; and proposal requirements for specific changes and updates." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Regulatory challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The governance model addresses three primary regulatory challenges: " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Making it clear that the Laconic LNT token is solely a loyalty point or prepayment for services—not a security or currency" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ensuring that on-chain private auction of membership interests in a Cayman Island LLC is fully compliant with US securities law" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Protecting minority rights for all shareholders of Laconic LLC" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Ideological challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As Web3 scales, Laconic Network aims to become an essential layer in service of verified blockchain data to applications. The company's governance model must be designed to:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Reward active Network participants through incentives for actions that help build and run the network, including becoming Members or Service Providers, writing Watchers, and consuming network data" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Discourage attempts to speculate on, or otherwise passively profit from, the network" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Corporate structure" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic LLC structure lays out a flexible but binding legal framework for the company's relationship with Network Members. It also provides for treasury management and related off-chain governance solutions while the network is being built:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network, as a corporate entity, is a Cayman Islands LLC made up of members who are themselves corporations. Members work together to build and operate the Network, for which they are required to act as Service Providers and Validators; as a Validator, each member controls a share of the Laconic Network Liquidity Pool. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The corporation's purpose is to sell data in a way that provides purchasers with cryptographic evidence, which ensures reimbursement if a Service Provider fails to meet a quality of service guarantee. Each membership interest in Laconic LLC is indivisible and nontransferable, and represents an actual security per the S.E.C. definition of the term." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On-chain membership interests are determined based on a combination of each Member's capital accounts and individual Liquidity Pool shares—a structure designed to reward members for acting as Validators and Service Providers while promoting healthy competition. The Members’ treasury collateralizes the Network, protecting Service Providers and users." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network currently has seven Founding Members, who brought the project into existence by funding its treasury and providing the engineering work to create the Laconic Stack and Laconic App. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Founding Members have been selected to ensure enough initial Validators and Service Providers to run the hardware needed to index and serve data to end users." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Requiring that Members both fund the Network and provide technical services ensures that all Members are motivated to make the project successful. There is no passive investment—for example, from VCs who buy the token at a discounted price to fund the Network, then wait for a pump to sell. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Selecting seven Founding Members supports accountability for a minimum viable level of decentralization. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members operate within multiple legal jurisdictions, preventing the Laconic Stack, Validators, and Service Providers from concentrating in too few data centers or cloud providers." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As demand for the services provided by Laconic Network increases, it will be necessary to add Members in order to increase the capacity and diversity of available Service Providers. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As Members are added, Membership totals must always take the form of 6n+1—ensuring that simple majority votes will never be evenly split, and that Validators will never be paid for Byzantine fault tolerance (BFT) that they do not provide. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "New Members are added via auction. To participate in an auction, potential Members must prove that they are technically qualified and have the funds needed to buy a Membership Interest from one or more existing members." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To demonstrate their ability to operate the Laconic Stack at a level of quality comparable to that of existing Members, potential Members must participate in a testnet." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Potential Members who successfully complete the testnet are invited to a private auction of a lot of six new Membership Interests. Auction proceeds are added to the Liquidity Pool, minus a Seller’s Reward for existing Members. Seller’s Rewards, akin to mini liquidity events, provide additional financial motivation for membership. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "At some point in the future, Laconic LLC may choose to change the rules regarding membership and Validator participation." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As required by law, provisions exist by which Members may be voluntarily or involuntarily removed from the Network in order to maintain minority shareholder rights of the membership as a whole." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members wishing to exit may sell their interest back to Laconic LLC, which will then auction it to a new potential Member. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members failing to fulfill Validator or Service Provider duties may be evicted by a governance resolution. " + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Laconic Network Token" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Much of the mechanism design of the Laconic Network is encapsulated in the intended uses of the Laconic Network Token (LNT)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": " LNT provides insurance to the Network's Service Providers and data consumers. Any prepayment for services, or any surplus services that data consumers have paid for but not used, may be redeemed for an asset of value. For Service Providers, LNT simplifies business operations, serving as a single asset representing claims against both a stablecoin and the native currency of the L1 on which they are required to operate. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The LNT also provides guarantees to Network participants, in much the same way that initiating a retail transaction triggers an automatic temporary hold on the buyer's payment card, which is released when the purchase is completed. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note that  LNT’s purpose is to allow for refundable prepayment of services among a federation of Service Providers operating across multiple jurisdictions; the token does not exist in any other capacity. It is not a security, as it does not represent a financial interest in Laconic LLC; it can be redeemed only via authorized transfer within the Liquidity Pool, which is controlled by the Laconic Members. Membership Interests are, however, securities, as they represent both the financial interests and the governance stakes of the Members in Laconic LLC; one Membership represents one vote." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The LNT is considered a voucher for data retrieval, not a general-purpose currency. LNT is procured through the Liquidity Pool by trading 3CRV or ETH, and used to pay Service Providers for data transfers. LNT is also staked by Service Providers, and enables them to register on-chain as such. A reclamation function ensures that the full fixed supply of the token will remain constant and in use at all times, making it unsuitable for long-term holding or speculative investment. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Corporate governance for Laconic LLC will be performed on chain, by voting parties who are both Members and Validators. Voting parties may vote on two types of resolutions:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Ordinary resolutions" + }, + { + "type": "span", + "value": " govern operational details of the network and require a ⅔ vote to pass." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Special resolutions" + }, + { + "type": "span", + "value": " are required to amend the governance articles or shut down the Network. Special resolutions must be passed by a unanimous vote." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Proposals may be submitted by any user of the Network who has more than the equivalent of $1000 USD escrowed as LNT." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "New possibilities for organizational governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s primary goal is to make verifiable blockchain data available at scale to Web3 applications. The company also aims to embody the ideological goals of true decentralization and fair rewards for parties actively participating in the network as Members, Validators, Service Providers, or Watcher writers. The Founding Members have also thought extensively about the utility of the LNT token (as a voucher for data), its role in governance (none at all), and how to grow the network through Validator/Member auctions. Together, these choices yield a novel corporate governance model worthy of study by any organization with similar goals and challenges." + } + ] + } + ] + } + } + }, + "date": "2022-10-25", + "featured": false, + "id": "55641249", + "image": { + "url": "https://www.datocms-assets.com/66113/1664245622-governance-blog-4.png" + }, + "slug": "laconic-governance-model", + "title": "A New Governance Model for the New Web" + }, + { + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "category": [ + { + "slug": "news", + "title": "News", + "id": "2965426" + } + ], + "content": { + "blocks": [ + {}, + {}, + {}, + {}, + {}, + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Hello Laconians," + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Welcome to our first Laconic Monthly Update! " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth development, we were excited to go live with our public marketing launch in the last quarter." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the three months since, Laconic has grown quickly, and we’ve made huge progress with both product and growth. Despite the arrival of “crypto winter,” our team remains focused on building a platform with the power to ensure that the next generation of blockchain applications are safer, faster, and more useful." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thank you for joining us on our journey!" + } + ] + }, + { + "item": "63259974", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "METAMASK PARTNERSHIP" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This month, we announced that Laconic is partnering with the world’s leading self-custodial wallet, ConsenSys’s MetaMask, and MetaMask founder Dan Finlay, to launch MobyMask, an anti-phishing tool. Laconic's MobyMask-caching Ethereum light client greatly reduces the cost of hosting a trustworthy copy of the MobyMask anti-phishing registry, making the tool accessible and affordable for a wide range of users." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Together, we’re laying the groundwork for an ambitious shared vision: a system with the ability to verify credible sources for a wide range of information types. You can " + }, + { + "url": "https://www.laconic.com/blog/laconic-and-consensys-metamask-launch-mobymask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "read our blog to learn more about the partnership with MetaMask" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259975", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TESTNET" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve successfully launched a testnet for internal development and for testing with Laconic’s Founding Members (Founding Members to be announced). The genesis transaction occurred on August 9, 2022, with all Members in consensus.\n\nWe’re now creating tooling to monitor block production, number of transactions, and Validator health; further tooling will include a dashboard and a block explorer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A public, incentivized testnet is planned for 2023. You can sign up for early access " + }, + { + "url": "https://www.laconic.com/partners#testnet", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "here" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259976", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "LIQUIDITY POOL & PAYMENT CHANNELS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve continued work on the mechanics and logistics of the liquidity pool, state/payment channels that will govern the flow of payments and tokens between our own chain and Ethereum, and auction logic implementation. We’ll also be building out the back end of the Laconic App and its network underpinnings. The next step is to deploy the smart contract for the liquidity pool directly to testnet. " + }, + { + "url": "https://www.laconic.com/blog/introducing-laconic-network", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Learn more about our platform and network" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259977", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PUBLIC MARKETING LAUNCH" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth, we completed the design and development of the Laconic website and community channels, with a successful public launch on August 16. Since then, we’ve scaled online engagement with potential partners, developers, press, and people around the world looking to learn more about Laconic. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "item": "63260091", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WEEKLY GROWTH CAMPAIGNS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve launched 14 campaigns since the website launch, and will continue to scale marketing and community efforts in 2023." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "July 26:" + }, + { + "url": "https://www.laconic.com/blog/introducing-laconic-network", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Introducing Laconic Network" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "July 26:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-different", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Network is Different" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "August 6:" + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watchers: Ensuring Trustlessness in Web3" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "August 30:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Network Uses IPLD" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 6:" + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watchers: Ensuring Trustlessness in Web3" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 13:" + }, + { + "url": "https://www.laconic.com/blog/rick-dudley-on-interchain-fm", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Rick Dudley Discusses Laconic Network on Interchain.fm" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 20:" + }, + { + "url": "https://www.laconic.com/blog/what-is-a-proof", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "What Is a Proof and Why Do You Need One?" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 28:" + }, + { + "url": "https://www.laconic.com/blog/99-problems-but-nfts-aint-one", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "99 Problems But NFTs Ain’t One" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 5:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-improves-the-nft-experience", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Radically Improves the NFT Experience" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 20: " + }, + { + "url": "https://www.laconic.com/blog/laconic-devcon-vi-recap", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic’s Devcon VI Recap" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 25: " + }, + { + "url": "https://www.laconic.com/blog/laconic-governance-model", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "A New Governance Model for the New Web" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "November 8: " + }, + { + "url": "https://www.laconic.com/blog/laconic-and-consensys-metamask-launch-mobymask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic and ConsenSys’s MetaMask Launch MobyMask" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "November 22: " + }, + { + "url": "https://www.laconic.com/blog/why-decentralization-matters", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Why Decentralization Matters" + } + ] + } + ] + } + ] + } + ] + }, + { + "item": "63259978", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "EVENTS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Nothing beats real-life engagement! Conferences and events are key channels for growing audience awareness. Our team canvassed select Ethereum and blockchain conferences with a focus on brand positioning, partnerships, and customer development:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ETHAmsterdam and Devconnect (4/18–25): " + }, + { + "type": "span", + "value": "Going into the launch of our public marketing channels, our team produced " + }, + { + "url": "https://twitter.com/laconicnetwork/status/1521957074841706497", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "a networking event" + } + ] + }, + { + "type": "span", + "value": " to connect with Member teams, investors, and developers attending ETHAmsterdam and Devconnect, a conference dedicated to Ethereum L2 Scaling." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ETH CC Paris (7/18–22): " + }, + { + "type": "span", + "value": "Multiple Laconic Member teams converged at the largest Ethereum conference. This was a great opportunity for us to deepen awareness of Laconic among investors and validators in the Ethereum community. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cosmoverse Medellin (9/27–28): " + }, + { + "type": "span", + "value": "Cosmos’s largest conference was an opportunity for us to build awareness and engagement in the Cosmos ecosystem." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Devcon Bogota (10/7–16): " + }, + { + "type": "span", + "value": "The largest Ethereum developer conference offered the perfect opportunity to drive awareness of Laconic with Ethereum developers. Check out the " + }, + { + "url": "https://www.laconic.com/blog/laconic-devcon-vi-recap", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "recap blog" + } + ] + }, + { + "type": "span", + "value": " to learn what the hot topics were at Devcon." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "SF Blockchain Week and ETH SF (11/1–5): " + }, + { + "type": "span", + "value": "These two Bay Area conferences enabled our team to deepen our network in one of the world’s premier tech hubs." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\n\n" + } + ] + } + ] + } + } + }, + "date": "2022-12-01", + "featured": true, + "id": "63259979", + "image": { + "url": "https://www.datocms-assets.com/66113/1669870811-laconicupdate-blog.png" + }, + "slug": "monthly-update-dec-2022", + "title": "Laconic Monthly Update: December 2022" + }, + { + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "category": [ + { + "slug": "insights", + "title": "Insights", + "id": "6311819" + } + ], + "content": { + "blocks": [ + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The emergence of any technology brings with it new language, and changes or expands the possible meanings of existing terms. Blockchain, of course, is no exception—and almost any discussion of it will include lots of talk about decentralization. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We should probably start with a definition. In the context of blockchain, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralization" + }, + { + "type": "span", + "value": " denotes the transfer of ownership and governance to the many from the few. Think of the differences between Prince, a solo songwriter and performer who insisted on absolute and final control over his artistic output, and the Beatles, a band that (despite their well-known internal power struggles) had no designated “lead” singer or sole songwriter. While decentralization may not require absolute accord, it does locate power across a system rather than in one designated point. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s worth noting that many in the Web3 world use the terms " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralized " + }, + { + "type": "span", + "value": "and " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distributed" + }, + { + "type": "span", + "value": " interchangeably" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": ", " + }, + { + "type": "span", + "value": "but this is technically incorrect. In the context of the blockchain, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distributed" + }, + { + "type": "span", + "value": " refers not to ownership or governance, but only to the distribution of parties (in most cases, servers) across a physical or digital space.\n\nIn this context, decentralization isn't a binary value. Instead, it describes the values and characteristics of a system that deliberately locates significant aspects of power across a defined system, rather than at a single point within it. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "But, like, what does decentralization " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "do?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is one of the primary innovations that powers blockchains. As the number of parties participating in the consensus mechanism of a blockchain increases, so does its level of decentralization. Participants' consensus about a given digital truth replaces the need for individual trust agreements, while establishing credible censorship resistance for anyone who wants to " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "do something" + }, + { + "type": "span", + "value": " on that blockchain. Any transaction is agreed upon by all, which allows any two parties to transact without first establishing a specific shared trust. This removes the need for oversight by a third party—for example, a \"financial panopticon\" established to prevent fraud—and reduces associated privacy and security risk. " + } + ] + }, + { + "item": "61461493", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization plays a key role in the Blockchain Trilemma—the need to balance decentralization, security, and scalability. A blockchain's level of decentralization is directly proportional to its ability to withstand attacks from inside or outside the network, and directly correlated with its level of neutrality, with greater decentralization linked to stronger censorship resistance and spam-resistant pricing. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most exciting things about decentralization is that the digital truth agreed upon via consensus may be proven by anyone running a node. Network participants must uphold the established consensus when making new blocks, or their stake will be slashed—they'll lose a significant portion of the benefits of entry. And while block producers are referred to as " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "validators" + }, + { + "type": "span", + "value": ", they perform no defined validation action. Instead, they have a financial incentive to act upon the truth determined by a decentralized quorum of participants." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As the number of participants in a blockchain increases, so does the power of decentralization, preventing participants from stealing someone else’s assets by manipulating transactions submitted for block inclusion. Each member controls the private key for a specific address, so no one can censor or manipulate a transaction once it’s been submitted. In the broadest sense, this system operates counter to centralized payment protocols such as PayPal, which censors transactions and even " + }, + { + "url": "https://www.forbes.com/sites/emilymason/2022/10/27/after-paypal-revokes-controversial-misinformation-policy-major-concerns-remain-over-2500-fine/?sh=2e5a914c30c4", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "levies fines on users for not obeying a set of internally derived rules." + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Until recently, says Laconic cofounder and chief protocol designer Rick Dudley, “we’ve mostly been concerned with usurious intermediaries. So maybe it’s best to think about " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "disintermediation" + }, + { + "type": "span", + "value": " as a primary goal within a decentralized system.\" Profit-driven intermediation, he notes, \"is also far more difficult to add to a well-decentralized system, which resists usury by nature.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Look, I don’t care about jpegs or digital Monopoly money. What’s in it for me?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization doesn't just increase the security of financial assets ... or ape GIFs No one party controls the decentralized network, so " + }, + { + "url": "https://www.coindesk.com/business/2022/06/29/coinbase-is-reportedly-selling-geo-location-data-to-ice/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "your pedigree information can't be given or sold to corporations, governments, or any other third parties without your consent" + } + ] + }, + { + "type": "span", + "value": ", as is dismayingly common on Web2 platforms. Decentralization can also protect network members from arbitrary moralities of a market subset—think the ejection of sex workers from Craigslist, protesters losing their financial platforms under political pressure, or citizens prevented from transacting by their government." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Anything else?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization also maximizes data reliability. A single point of failure can affect huge swaths of the public; imagine the effects of destroying the only server farm servicing a specific bank. Of course, to mitigate the risks associated with power outages, simple server failures, or even terrorist attacks, banks geographically " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distribute" + }, + { + "type": "span", + "value": " their servers, with multiple parties storing multiple copies of data in multiple places across a " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralized" + }, + { + "type": "span", + "value": " network. " + }, + { + "url": "https://en.wikipedia.org/wiki/Byzantine_fault", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Even if a large portion of its participants were to go dark, the network would still function." + }, + { + "type": "span", + "value": "\n\n" + } + ] + }, + { + "type": "span", + "value": "In this scenario, the bank is protected—though you, as a user of the bank, are vulnerable to having your transactions watched, censored, or thrown out altogether. The contents of your account can also be " + }, + { + "url": "https://ij.org/report/policing-for-profit-2/grading-state-federal-civil-forfeiture-laws/irs-cleans-out-bank-accounts/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "seized for potentially arbitrary or extralegal reasons" + } + ] + }, + { + "type": "span", + "value": ". As the recent " + }, + { + "url": "https://cointelegraph.com/news/bitcoin-sinks-to-new-yearly-low-at-16-8k-as-ftx-insolvency-fears-turn-into-contagion", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "FTX meltdown" + } + ] + }, + { + "type": "span", + "value": " made all too clear, centralized cryptocurrency exchanges are even more precarious—and because of their inherent lack of regulation, " + }, + { + "url": "https://www.forbes.com/sites/haileylennon/2022/08/01/bankrupt-crypto-lender-celsius-could-leave-customers-last-in-line-to-get-paid/?sh=4c4005ea5fde", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "prone to leave retail investors holding the bag" + } + ] + }, + { + "type": "span", + "value": ". A more decentralized system could offer individual users far more protection against institutional controls, not to mention far less potential for reckless mismanagement by those holding the keys." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Still a long way to go" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralizing a protocol or network still poses many challenges. Foremost among them is the fact that " + }, + { + "url": "https://www.weforum.org/agenda/2016/05/joseph-stiglitz-are-markets-efficient-or-do-they-tend-towards-monopoly-the-verdict-is-in/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "markets tend to direct capital to the few and not the many" + } + ] + }, + { + "type": "span", + "value": ". Our current way of life is undergirded by a system that counters decentralization at every level, moving power away from the majority. Accepting decentralized systems in the physical world would require changing much of how we think about how society is arranged, and about " + }, + { + "url": "https://www.latimes.com/opinion/op-ed/la-oe-pascrell-live-nation-concert-ticketing-20180517-story.html", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "rapidly shrinking" + } + ] + }, + { + "type": "span", + "value": " access to competition under late capitalism." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Another key point: Governments that cannot monitor private transactions often attempt to sanction users of protocols that protect the anonymity afforded by robust, decentralized censorship resistance. Such moves can be used to" + }, + { + "url": "https://home.treasury.gov/policy-issues/financial-sanctions/sanctions-programs-and-country-information/iran-sanctions", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "uphold international sanctions" + } + ] + }, + { + "type": "span", + "value": "—or " + }, + { + "url": "https://www.wsj.com/articles/crypto-advocacy-group-sues-u-s-treasury-over-tornado-cash-sanctions-11665610506?mod=article_inline", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "oppress a government’s own citizenry" + } + ] + }, + { + "type": "span", + "value": ". " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most glaring problems of decentralization is Web3 spaces' level of volatility. While money laundering has existed since the beginning of money (and would continue even if all the blockchains in the world were to vanish), there's enough money laundering, wash trading, and socially engineered theft on blockchain networks to lead regulators to sanction, " + }, + { + "url": "https://www.coindesk.com/policy/2022/08/21/arrest-of-tornado-cash-developer-draws-dutch-crypto-community-protest/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "or even imprison" + } + ] + }, + { + "type": "span", + "value": ", participants and even developers—while hinders true decentralization across the ecosystem. If we want to make the ecosystem safe for everyone, we've got a lot of housecleaning to do." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "So where does Laconic come in?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is a core feature of the Laconic Network, delivering all of the benefits we've described—uptime, security, affordability, censorship resistance, and fair, consensus-driven governance. The network's seven founding Members, and 60+ additional Members spread across multiple jurisdictions, each run their own hardware, making it highly resistant to concentrated server failures, tampering by individual bad actors, and hostile takeovers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Each Member serves the network by running " + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Watchers," + } + ] + }, + { + "type": "span", + "value": " smaller caches of specific data commonly used by, for example, DApp developers. There's no single point of failure—if any one Service Provider fails, other Members can ensure Watcher uptime." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization will drive even more benefits as the network matures. Laconic Network customers can purchase data directly from Service Providers, which compete to win customers through low prices, at the same time exerting downward pressure on data prices across the industry." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, while geographically diverse traditional networks can be subject to an incredibly complex web of national and local laws, participants in the decentralized Laconic Network need comply only with those laws governing their own jurisdictions. “In each jurisdiction,\" explains Dudley, \"varying and often contradictory regulations or sanctions can interfere with or contradict one another, making global compliance difficult. The goal is to be compliant in our jurisdiction, while allowing our users to be compliant in each of theirs.” Each Laconic Stack user decides how best to comply with the laws of their jurisdiction. The biggest benefit here, though, is that under this model, no single governing body has the power to determine a specific flow of data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is at the heart of Web3, and key to all major benefits of the blockchain. As a structure, it has the power to offer the most benefit to the largest group of people, making it more difficult for the few to hoard power or capital at the expense of the many. But for blockchain, and the larger Web3 ecosystem, to reach its full potential, we must work toward broad understanding of the benefits and pitfalls of decentralized networks. Only then can we begin to calm the waters of Web3, and remove the sharks for the next billion users we hope to bring with us." + } + ] + } + ] + } + } + }, + "date": "2022-11-22", + "featured": true, + "id": "61461416", + "image": { + "url": "https://www.datocms-assets.com/66113/1669073122-central_decentral_009.png" + }, + "slug": "why-decentralization-matters", + "title": "Why Decentralization Matters " + }, + { + "author": { + "id": "24856058", + "name": "Maly Ly" + }, + "category": [ + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [ + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "item": "38579992", + "type": "block" + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Vision" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We believe that Web3 and blockchain technologies offer the promise of " + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "permissionless and equal access" + }, + { + "type": "span", + "value": " to new digital tools and financial instruments, and that these properties can be a force for positive change. Critically, decentralization of the networks and resources in Web3 is the key protection against the monopolistic jurisdiction of large tech companies and overzealous governments.  " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We further believe that the creation of public, consensus driven, cryptographically verifiable data leads to greater transparency and accountability. Finally, we believe that transacting on blockchains fosters incentive alignments through tokenomic engineering, allowing us to be more deterministic about how systems and society run." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These are the core values that imbue all of the design decisions in creating the Laconic Network. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The challenges facing Web3 adoption are significant, and threaten to prevent the fulfillment of the promise and vision. Laconic Network is focusing on a set of challenges that arise when DApp developers need to interact with blockchains and query the data that is stored there. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Working with blockchain data is more challenging than traditional web development because of the storage constraints, the overhead of preserving consensus and protecting networks from attack. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Other significant challenges include: " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Blockchain nodes are designed to be write-optimized and bear large overhead costs for storage, network gossip, and computation required for consensus and security." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "True internet scale growth (eg. comparable to Facebook or Twitter) is not possible on existing Web3 backend infrastructure. Mass adoption will require the development of a data scalability layer that facilitates for Web3 the types of usage patterns that we expect from Instagram, Google, and LinkedIn." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These challenges all compound when cross chain data is called for, which is increasingly the case. The data scalability layer will also need to account for cross-chain interoperability." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic isn’t the first to address the challenge of building a data availability layer for Web3. Companies such as Infura and Alchemy have long offered blockchain data services as convenient APIs for DApp developers, and have thus facilitated the early growth of high utility applications that interact with the underlying chains. As centralized services with quickly growing traction and clout, they already in some ways resemble the monopolistic centralized players of Web2. The convenience they offer is paid for at the price of losing censorship resistance and permissionless access. In short, we trust these companies to tell us the truth and treat us right. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Somewhat truer to the ethos of blockchain and Web3 are The Graph and Covalent, both of which have recently launched products that have elements of decentralization and verification of the data being served. For Ethereum, these services are limited to indexing events, transactions, and internal transactions, and don’t typically index the state and storage trees at all. One consequence of this is that they don’t support the Ethereum JSON RPC get_proof() call, which is essential to safeguarding the verifiability of the data being queried. No proof, no trustworthy data. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Why will Laconic’s solution solve current challenges?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network is innovating on three separate fronts: technology, governance, and incentive alignment. The challenge of serving verifiable blockchain data, at scale, from a decentralized platform is so enormous that a purely technical solution won’t suffice. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Technology" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "At a technology level, we have pioneered multiple innovations. " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD structures to preserve the hash-linked data format that is native to blockchain data—even withstanding transformations." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The concept of the Laconic Full Index Node which removes the need to re-index every time a new data access use case is materialized. The result is significantly faster setup times for customized data access API endpoints, that are tailored to individual DApps." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Re-indexing of blockchain data into specialized caches (Watchers) to simplify anticipated DApp use cases. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Content-addressability of blockchain data. In the same way that IPFS is used to request files by content hashes, blockchain data will be requested by hashes of its content, and served through the off-chain p2p IPFS network. " + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "This leads to a global hyperscale caching layer that is sufficient enough to ensure the viability of Facebook or Google level adoption of Web3." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Working with multiple international legal teams, Laconic Network is pioneering a novel governance model with the aims of ensuring unprecedented decentralization, fairness, and equality. The goals are simple: maintain a decentralized network that resists being monopolized and which is not subject to the whims of any single entity’s jurisprudence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In order to achieve those goals, a number of problems must be addressed:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A sufficient number of Validators and Service Providers to scale as Web3 grows" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A rich diversity of underlying network and data center dependencies (e.g. we aim to avoid ending up with a preponderance of machines running on AWS)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A diversity of jurisdictional locations for network Members" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A diversity of funding sources for network members" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All governance decisions occur on-chain" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All members have equal voting power" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "No early token allocations for VCs or founders" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Incentive Alignment" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the prime benefits of blockchains is the ability to prescribe token incentives that align with desirable behaviors. For example, validators are incentivized to do the work needed to secure the network and establish consensus about block generation. The Laconic Network has created a number of incentives to align the many roles involved towards an ever more efficient and growing data availability layer for Web3." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For data consumers, it’s important to have a true selection of Service Providers (node operators who run Watchers). Since Service Providers must compete with each other on the basis of price and service level, this benefits consumers by guaranteeing the best level of service for the lowest price." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Developers writing Watchers (to be run by Service Providers), are incentivized to create useful Watchers. There is a mechanism for rewarding developers when people choose to run and query the Watchers they have written. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Members have a strong incentive to grow the network to meet demand through the fulfillment of new member auctions. These auctions bring new liquidity to the network as well as new members who can increase network capacity by indexing and serving data. A bonding curve rewards existing members with an auction fee with the earliest members benefiting the most. However, there is a disincentive to growing the network too quickly, as earnings for Members will come from indexing and serving data. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Anyone interested in building out the network will have a token incentive and opportunity to participate simply by running an in-browser cache of the off-chain data. By caching the data in this way, it will form an infinitely scalable global hyper-cache of the data, and the service level of DApps will increase as a result. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Conclusion" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic aims to significantly contribute to the growth and adoption of Web3 and blockchain technologies by solving foundational problems that currently hamper the development of DApps. This involves indexing blockchain data and making it available from a decentralized network of service providers, and doing so in a way that retains the cryptographic verifiability of that data. The innovations that allow us to do this emanate not only from technological advances, but also from the novel governance and incentivisation structures that are built into the very network structure. " + } + ] + } + ] + } + } + }, + "date": "2022-07-26", + "featured": true, + "id": "35743439", + "image": { + "url": "https://www.datocms-assets.com/66113/1657616658-laconic_differentiators_7.png" + }, + "slug": "how-laconic-different", + "title": "How is Laconic different?" + }, + { + "author": { + "id": "24856058", + "name": "Maly Ly" + }, + "category": [ + { + "slug": "partners", + "title": "Partners", + "id": "3545001" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "blockquote", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "\"It’s hard to overstate what an achievement this [Laconic] is for bringing down the computational costs of privacy, scalable data distribution, and also speed of data lookups for users who repeatedly use the same contracts but don’t necessarily run their own full nodes.\"" + } + ] + } + ], + "attribution": "Dan Finlay, MetaMask Founder" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Today I’m thrilled to announce that " + }, + { + "url": "http://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-clienthttps://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic is partnering with the world’s leading self-custodial wallet, ConsenSys’s MetaMask" + } + ] + }, + { + "type": "span", + "value": ", and MetaMask founder Dan Finlay, to launch " + }, + { + "url": "https://mobymask.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "MobyMask" + } + ] + }, + { + "type": "span", + "value": ", an anti-phishing tool. You can check out Dan Finlay’s " + }, + { + "url": "https://mirror.xyz/0x55e2780588aa5000F464f700D2676fD0a22Ee160/8whNch3m5KMzeo6g5eblcXMMplPf8UpW228cSh3nmzg", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "original MobyMask blog" + } + ] + }, + { + "type": "span", + "value": ", and his " + }, + { + "url": "https://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-client", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "blog announcement today" + } + ] + }, + { + "type": "span", + "value": ", for more information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MobyMask is a community-sourced registry for managing and reporting phishing accounts across social media and Web3 intersections, based on MetaMask's " + }, + { + "url": "https://mirror.xyz/0x55e2780588aa5000F464f700D2676fD0a22Ee160/pTIrlopsSUvWAbnq1qJDNKU1pGNLP8VEn1H8DSVcvXM", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Delegatable framework" + } + ] + }, + { + "type": "span", + "value": ". It provides robust tools for sourcing phishing reporters from across online communities, using a dynamic web of trust. The registry is designed to grow organically as community members report scammers and phishers directly onto the Ethereum mainnet." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Accessible, affordable privacy and security" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic's MobyMask-caching Ethereum light client greatly reduces the cost of hosting a trustworthy copy of the MobyMask anti-phishing registry, making the tool accessible and affordable for a wide range of users. By optimizing the blockchain query process, explains Dan Finlay in his blog announcement today, Laconic \"greatly reduces the computational costs of privacy, scalable data distribution, and also speed of data lookups for users who repeatedly use the same contracts but don’t necessarily run their own full nodes.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MobyMask further streamlines blockchain security by bringing light-client functionality directly to the browser. The key is the " + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watcher" + } + ] + }, + { + "type": "span", + "value": ", an essential piece of the Laconic Stack that caches only the specific blockchain data required for a particular query—reducing data requirements by orders of magnitude while creating a lightweight, self-hostable process that web services such as MetaMask, WalletGuard, and Phishfort can use to draw MobyMask phishing detection data. A soon-to-be-released TypeScript version of our Watcher will streamline security even further, with support for fully browser-based list caching and peer-to-peer data replication. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Lowering barriers for entry to Web3 " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Phishing and related scams plague both novice and experienced blockchain users, creating further barriers to adoption and limiting the potential for growth of the Web3 ecosystem. That’s created a real need for tools that make this ecosystem safer, faster, easier, and more affordable to use. In reducing technical requirements for interacting with the blockchain, the Laconic protocol lowers barriers to entry, making Web 3 technologies more accessible to those with limited resources." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With that goal in mind, Laconic is working on an update that lets users search a dynamic repository of phishing reports, and share them with a network of peers, via a free private API. Messages will be fully provable on chain, with blockchain used only to resolve registry conflicts and revoke access when needed. While the initial repository is by invitation only, Laconic and MobyMask plan to eventually allow users to subscribe to multiple roots of trust, and to host their own. Meanwhile, we’re laying the groundwork for an ambitious shared vision: a system with the ability to verify credible sources for a wide range of information types." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "A highly scalable and privacy-preserving application" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With MobyMask making Web3 access safer and less stressful for everyone involved, the potential benefits, says Dan, are substantial. ”If we’re going to build anything of value out of decentralized technology, we need to basically eliminate phishing. That's going to take a lot of creativity and ingenuity, and we’re happy that " + }, + { + "url": "https://www.laconic.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic" + } + ] + }, + { + "type": "span", + "value": " and MobyMask combine so well to deliver a highly scalable and privacy-preserving application whose safety remains rooted on the blockchain.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nYou can learn more about MobyMask at " + }, + { + "url": "http://www.mobymask.com", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "mobymask.com" + } + ] + }, + { + "type": "span", + "value": ". For more information on Laconic, " + }, + { + "url": "https://discord.com/invite/ukhbBemyxY", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "join the Laconic Discord" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + } + ] + } + } + }, + "date": "2022-11-08", + "featured": true, + "id": "56055548", + "image": { + "url": "https://www.datocms-assets.com/66113/1667863936-partnership-template-updated.png" + }, + "slug": "laconic-and-consensys-metamask-launch-mobymask-light-client", + "title": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client" + }, + { + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "category": [ + { + "slug": "insights", + "title": "Insights", + "id": "6311819" + } + ], + "content": { + "blocks": [ + {}, + {}, + {}, + {}, + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As the first Devcon held in 3 years, and right after the historic Merge, Devcon VI drew thousands of attendees from around the world and our team was fortunate to be a part of it.\n" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So what were the hot topics at this year’s Devcon VI in Bogota?" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "\n" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "1. Maximum Extractable Value (MEV)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most pervasive topics at Devcon was MEV. Flashbots co-founder Phil Daian announced the launch of SUAVE, Single Unifying Auction for Value Expression, to combat MEV centralization and censorship.\n\nSUAVE will be an MEV-aware encrypted mempool that maximizes profits for users. " + } + ] + }, + { + "item": "55844240", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "2. Office of Foreign Assets Control (OFAC)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Post Merge, OFAC compliant MEV boost relays have been enabled across a broader array of block proposers resulting in a censorship of 51% of Ethereum blocks. The implication is that if block producers can censor tx’s due to OFAC compliance, what other types of transactions could they censor?" + } + ] + }, + { + "item": "55844241", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "3. Regulation: Digital Commodity Consumer Protection Act (DCCPA) " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Regulation isn’t keeping up with the speed of innovation. This is obvious for instance in Europe’s current attempt to to regulate dollar-pegged stablecoins, capping volumes and potentially banning algo-stablecoin use. Stablecoin volume has increased dramatically over the last several years and a cap or ban could significantly hurt the crypto ecosystem overall.\n\nThe Digital Commodity Consumer Protection Act (DCCPA) could have a significant impact on DeFi in the US as well. The general consensus is that the crypto community as a whole has to advocate for positive outcomes, and resource groups like Coin Center that are actively fighting for regulatory guidance. " + } + ] + }, + { + "item": "55844702", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "5. Layer 2" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unsurprisingly, the increasingly crowded and competitive L2 landscape was on display.–The Arbitrum and Optimism booths  angled for attention side by side at the conference, an example of the escalating war for dominance between the major Layer 2 solutions. There was discussion around the security tradeoffs between side chains like Polygon and rollups like Arbitrum & Optimism, along with concerns that rollups are currently too centralized and lacking fraud proofs. " + }, + { + "url": "https://www.laconic.com/blog/what-is-a-proof", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Laconic knows a thing or two about proofs" + } + ] + }, + { + "type": "span", + "value": "…\n" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "6. Data Scaling" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Data indexing and scaling continues to be a fundamental challenge for Web3 development. You’re either running a full node yourself or forced to make compromises while using one of a handful of existing centralized indexing solutions.\nEnter Laconic, the first multichain verifiable data indexer. Laconic enables developers to build internet-scale Web3 applications and light clients at a fraction of the speed and the cost of current tools." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "*Side note, the conference wifi pw was “runfafullnode” but we’re here to do that for you…IYKYK.\n" + } + ] + }, + { + "item": "55846103", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Interestingly, the decision to host the conference in South America was prescient as the economic crises in Venezuela and Argentina highlight the use cases of crypto as a hedge against inflation and more generally as a means for financial sovereignty. We spoke with a number of Argentinians and Venezuelans about the growing usage of crypto payments in their countries.\n\nThe need to enable developers to build applications with access to faster and more reliable data has never been greater. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "item": "55844242", + "type": "block" + } + ] + } + } + }, + "date": "2022-10-20", + "featured": false, + "id": "55844243", + "image": { + "url": "https://www.datocms-assets.com/66113/1666289454-devcon-blog.png" + }, + "slug": "laconic-devcon-vi-recap", + "title": "Laconic’s Devcon VI Recap" + }, + { + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "category": [ + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [ + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Part 1 of this two-part series discussed the five major implementation and integration issues plaguing DApp developers, along with an overview of how Laconic addresses each one:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "1. NFT data lacks consistency and integrity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "2. NFT data is fragmented and scattered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "3. For both NFTs and equivalent token formats, data types vary from blockchain to blockchain." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "4. NFT games require lightning-fast retrieval and scalability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "5. Data retrieved from multiple locations is difficult to verify. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now, in Part 2, we'll explore how these problems are playing out in the real world—and how Laconic tools and processes shield both users and developers from the effects of shifting and conflicting standards, radically reducing everyone's list of problems.   " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Apes, witches ... tax fraud? The power of caching." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's start by looking at a current cornerstone of the industry. Arguably the most popular use case for NFT artwork is " + }, + { + "url": "https://boredapeyachtclub.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "10k PFP collections" + } + ] + }, + { + "type": "span", + "value": ", collections of (typically 10,000) relatively simple anthropomorphized cartoon jpegs, generated by combining variations on a large but limited set of defined metadata “traits,” each of which correlates to a physical attribute of the generated character drawing. " + }, + { + "url": "https://boredapeyachtclub.com/#/home", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Bored Ape Yacht Club" + } + ] + }, + { + "type": "span", + "value": ", for example, uses this model. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the case of another popular collection, " + }, + { + "url": "https://www.cryptocoven.xyz/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Crypto Coven" + } + ] + }, + { + "type": "span", + "value": ", a smart contract is called to mint tokens based on a similar list of randomly generated traits. To gather each piece of this data and display it, a wallet client or platform makes an API call to an " + }, + { + "url": "https://docs.metamask.io/guide/rpc-api.html#table-of-contents", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "RPC" + } + ] + }, + { + "type": "span", + "value": " provider. To retrieve the data, the client or platform must sift through an extraordinary amount of data to find just a few essential pieces. And all that searching eventually adds up. More serious problems could also arise, from sandwich MEVs to lowball ape sales as a " + }, + { + "url": "https://www.cryptonews.net/news/nft/6583225/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "front for tax shenanigans" + } + ] + }, + { + "type": "span", + "value": ".\n\nLuckily, there's an easier (and less easily abused) option. A Laconic Watcher could cache all data related to a given smart contract—in this case, " + }, + { + "url": "https://cryptocoven.mirror.xyz/A622VSRm8-9oLzc8l3oFGmfnFUZQmDQ3Wx3ObhSlhsc", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the brilliant Crypto Coven `counters.sol` variant" + } + ] + }, + { + "type": "span", + "value": "—making it instantly available at any DApp request while preserving proof of authenticity. Because Laconic can prove metadata at the current block height, not several blocks behind like most current centralized data providers, all NFT data received is guaranteed correct. That makes for faster queries, lower costs, easier updates—a quality-of-life upgrade for developers. For a particularly busy smart contract such as Bored Ape Yacht Club, Laconic's data cache could also drastically improve the daily experience of thousands of users. And let's not forget the damage done to industry reputation by " + }, + { + "url": "https://nftevening.com/bored-ape-6462-sold-for-200/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the successful exploits of every \"fat-fingered\" Ape owner" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Where we’re going, we don’t need standards." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Smart contracts are incredibly popular for good reason. But there are " + }, + { + "url": "https://etherscan.io/address/0x53e4c0167ed855e96f562dbb911854d586f5cc07#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "countless" + } + ] + }, + { + "type": "span", + "value": " " + }, + { + "url": "https://etherscan.io/address/0x517e643f53eb3622fd2c3a12c6bfde5e7bc8d5ca#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "variations" + } + ] + }, + { + "type": "span", + "value": " out there, even on Ethereum. In fact, " + }, + { + "url": "https://www.smartcontractresearch.org/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "entire Web2 and Web3 developer communities" + } + ] + }, + { + "type": "span", + "value": " view this aspect of NFT technology as where its real beauty is revealed: the developer as artist, Making It New. Limit contract and metadata standards, the argument goes, and you'll hamstring developers' creativity, starving the ecosystem of innovative ideas. (Unaddressed standards are, of course, nothing new. Think back to the browser wars of Web 1.0, when entire websites ... just didn't work on Internet Explorer, and at many design shops, ensuring IE functionality would cost you a good 20% extra.)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is uniquely suited to solve the problems associated with nonstandardized token data retrieval. By caching token data into microchains of IPLD blocks, and serving it directly to DApps from the resulting decentralized content-addressable database in a unified format, the Laconic Watcher makes the entire issue moot. It displays the precise data queried, confirms that it's correct—and does so more quickly and with a more current state than any standard RPC service." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "An end run around fragmented and scattered metadata." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A Laconic Watcher makes it easy for developers to retrieve blockchain data from an indexed database; fetch off-chain data as needed; and correlate, merkleize, and cache the results. With a " + }, + { + "url": "https://etherscan.io/address/0xe6ddda1c3f1cb01aa5c86a21e8636deabfd1f013#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Crypto Coven smart contract" + } + ] + }, + { + "type": "span", + "value": ", for example, there's no need to concern yourself with both the smart contract and the media the tokenURI points to—a Watcher can tie them together in ways standard RPC providers can't, with a terrific signal-to-noise ratio, perfectly up-to-date state, and persistent uptime.\n\nWatchers define and expose precisely crafted APIs for DApps that consume specific data sets, such as a given smart contract. Allowing a DApp itself to focus solely on its primary mission of delivering the goods to the user eliminates the need for (often surprisingly heavy) query lifts." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Smooth token data retrieval and management, even from multiple blockchains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Non-fungible tokens exist on multiple blockchains, in varying degrees of complexity, in a range of programming languages, in line with wildly varying (if any) metadata standards. The result is, unsurprisingly, significant friction when it comes to token management. Laconic removes much of the drag by unifying and extending the possibilities of metadata once it’s been retrieved." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To maintain a unified format regardless of chain or data source, Laconic stores all blockchain data in content-addressable data formats. This allows for a level of global availability and extensibility that makes DApp development far more convenient. " + }, + { + "url": "https://www.laconic.com/blog/99-problems-but-nfts-aint-one", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Our hypothetical on-chain Library of Alexandria in Part 1" + } + ] + }, + { + "type": "span", + "value": " is made possible by Laconic’s uniquely decentralized, content-addressable format and the Watcher's computationally light footprint. Think of this chain-agnostic view as a Rosetta Stone for Web3 data." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic makes querying NFT data verifiable, fast, and affordable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic makes DApp access both faster and easier with its up-to-date, indexed, verifiable blockchain data and custom query and caching services; queries, too, become more affordable to develop and maintain. And in a time of ballooning NFT data stores and rapidly increasing demands on user experience, Laconic Watchers deliver data at a fraction of the cost of traditional data retrieval." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In short, the architecture of the Laconic Network delivers practical solutions to many of today's most pressing NFT challenges. Custom Laconic Watcher services can, for example:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collect data from multiple blockchains" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Process that data to make it consumable by DApps" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Preserve data verifiability across transformations " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Keep data up to date" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Streamline DApp data retrieval" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With these capabilities, the Laconic Network becomes an indispensable data querying and verification layer for any NFT-related service—and, more than any other approach to querying blockchain data, a resource with the capacity to help the entire industry thrive." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Read more about Watchers and the Laconic Network " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "here" + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Got questions? Join us on Discord." + } + ] + }, + { + "item": "55708950", + "type": "block" + } + ] + } + } + }, + "date": "2022-10-05", + "featured": true, + "id": "55708951", + "image": { + "url": "https://www.datocms-assets.com/66113/1664922912-laconic_nfts_003-2.png" + }, + "slug": "how-laconic-improves-the-nft-experience", + "title": "How Laconic Radically Improves the NFT Experience" + }, + { + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "category": [ + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [ + {}, + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ask most people what they know about NFTs, and it’s likely you’ll hear a lot about digital art, tokenized authenticity, proof of ownership, and/or a hot new asset class (or, depending who you hang out with, commodity fetishism and the death knell of late-stage capitalism). But hot takes aside, today’s market for generative artwork is only the tip of the iceberg. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whatever the use case—and the possibilities are vast—today’s NFT ecosystem is generating mountains of largely unstructured data, both on and off chain. And without established technical standards to ensure clear structure and verifiability, it’s all too easy for that data to become the stuff of developer nightmares." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "More possibilities, more problems." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As use cases for NFTs continue to expand beyond " + }, + { + "url": "https://superrare.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "1/1 works of art" + } + ] + }, + { + "type": "span", + "value": " and " + }, + { + "url": "https://boredapeyachtclub.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "10k pfp collections" + } + ] + }, + { + "type": "span", + "value": ", developers building applications that integrate on-chain tokens face a widening maze of challenges around data management and queries. . The power of an NFT lies in its ability to represent any unique entity, with today’s common use cases including in-game tokens, editioned generative artwork, event ticketing, and even " + }, + { + "url": "https://www.theblock.co/post/134923/artist-blows-up-lamborghini-to-make-nfts-in-protest-against-crypto-culture", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "physical works or goods" + } + ] + }, + { + "type": "span", + "value": ". Blockchain and non-fungible tokens, in this case, make use of smart contracts, which can be written in a variety of programming languages, using varying methods of metadata storage and retrieval. The digital asset to which a given piece of metadata refers, and the metadata itself, can each be located almost anywhere in the decentralized web—which is itself both immeasurably large and continuously expanding. \n\nCombine a diversity of programming languages and an absence of standardization, and you end up with some novel problems, mostly involving how to handle the sheer volume of data generated, how to locate it in the many places it might be stored (both on chain and off), and how to handle inconsistent data formats and structures. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network was created to address these challenges through shared standards that make it possible for DApp developers to quickly and intuitively integrate and manage disparate NFT data and assets. In this piece, the first of a two-part series, we look at five major NFT implementation and integration issues—along with what we’re doing to solve each one, so you can sleep at night. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "1. NFT data lacks consistency and integrity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The functionality of an NFT lies in the metadata describing the individual item it represents. That metadata can consist of traits describing the characteristics of a jpeg artwork, essential information about copyright and intellectual property, guidelines for the item’s intended presentation, or all of the above and more. Metadata can also address a broad set of questions:" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": " Am I allowed to use an NFT for commercial purposes based on IP and copyright? Can I play a specific video, given its file format and codec? Are there readable methods for rendering this generative work? Can I easily list this NFT on the larger NFT marketplaces, where I have the best chance of selling it for the best price and in a timely manner?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The rapid growth of the NFT market has further expanded the possible functions of metadata. Developers need an efficient way to retrieve all types of metadata, and to maintain correlation with their on-chain counterparts in provable, hash-linked data structures. Laconic Watchers—APIs that serve data from the Laconic Network—fill this need. The Watchers’ custom search and caching services collect variously constructed data and combine it into a unified form that DApps can interpret and use, without sacrificing data integrity." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "2. NFT data is fragmented and scattered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s not practical to use current blockchain technology as a data storage or retrieval protocol. It was designed primarily as a means for achieving trustless consensus, not as a data availability system. The assets to which NFTs refer are often stored in protocols such as " + }, + { + "url": "https://www.arweave.org/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Arweave" + } + ] + }, + { + "type": "span", + "value": " and " + }, + { + "url": "https://ipfs.tech/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "IPFS" + } + ] + }, + { + "type": "span", + "value": ", and pointed to by the NFT’s “" + }, + { + "url": "https://docs.openzeppelin.com/contracts/2.x/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "tokenURI" + } + ] + }, + { + "type": "span", + "value": ".”  In one recent example, complete rendering libraries are stored as " + }, + { + "url": "https://twitter.com/dhof/status/1569509636587528195?s=20&t=KUYxKh-vG7qFaTtCvhRObA", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "compressed, on-chain data URIs" + } + ] + }, + { + "type": "span", + "value": "; smart contracts then access these libraries to render fully on-chain generative artworks." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A problem emerges, however, whenever a DApp needs to access any of this information. Methods for retrieval and DApp ingestion vary wildly depending on data type and location. And while RPC services can locate data—for a price—in most cases there’s no measurable way to ensure its integrity. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Despite being the most lightweight element of the Laconic Stack, the Laconic Watcher has the power to alleviate the proof issue, by preserving evidence of proof across data transformations while querying a far smaller subset of data than is typically required." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "3. For both NFTs and equivalent token formats, data types vary from blockchain to blockchain." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Most of today’s NFTs are based on Ethereum, and most commonly written in Solidity. But zoom out for a wider view of the possibilities for both token types and blockchains, and the problem space increases correspondingly. On Tezos, for instance, smart contracts are most often written in SmartPy or LIGO, with third-place Michelson being a common low-level, domain-specific language. On Solana, Rust, C, and C++ are commonly used to compose smart contracts (referred to in the Solana ecosystem as “programs).” It’s safe to say that we have more than a small naming, language, and methodology mess on our hands in the blockchain ecosystem!\n\nLet’s imagine a DApp that tracks all NFTs representing a certain kind of media—books, for example—to provide an index of on-chain literature. It would need the ability to accurately interpret smart contracts from each chain, across widely varying programming languages, syntax, and metadata standards." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network drastically simplifies this process, offering developers a unified view of data while allowing DApps to agnostically query data from multiple blockchains from a decentralized, content-addressable database." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "4. NFT games require lightning-fast retrieval and scalability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The volume of NFT transactions is expected to rise significantly with the " + }, + { + "url": "https://tiktok.immutable.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "current influx" + } + ] + }, + { + "type": "span", + "value": " of " + }, + { + "url": "https://about.instagram.com/blog/announcements/instagram-digital-collectibles", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "institutional Web 2 players" + } + ] + }, + { + "type": "span", + "value": " into the Web 3 ecosystem. The problem: As transaction volume and speed increase, data availability with censorship resistance and proof of integrity becomes increasingly unsustainable. And blockchain-based games alone are poised to send NFT transaction volumes to stratospheric heights, with more and more in-game events and transactions driving mounting network traffic. For example, the popular game " + }, + { + "url": "https://godsunchained.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Gods Unchained" + } + ] + }, + { + "type": "span", + "value": " is already generating " + }, + { + "url": "https://chainplay.gg/games/gods-unchained/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "significant transaction volume" + } + ] + }, + { + "type": "span", + "value": "—and it’s just one of countless on-chain games. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The growth of games and social apps using on-chain transactions continues to expose issues with Ethereum’s scalability and speed. The problem is compounded by the fact that most indexing services are typically a few blocks behind with updates—too far back to have DApps react to in-game events on time. And that leaves players holding the bag, subjected to unnecessarily clunky and unwieldy gaming experiences." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike traditional blockchain indexing services, the Laconic Network is equipped to provide up-to-date blockchain data with trivial delay, for scenarios in which real-time data retrieval is essential to user experience." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "5. Data retrieved from multiple locations is difficult to verify. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A fundamental promise of blockchain is verifiability. And while on-chain data is verifiable " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "per se" + }, + { + "type": "span", + "value": ", NFT data can be stored in any number of data repositories. Meanwhile, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "using" + }, + { + "type": "span", + "value": " that data requires intermediaries such as DNS system records, traditional web servers, and files stored in Web2 datacenters. Every one of these exposes the data to the possibility of censorship, manipulation by bad-faith actors, or simple disappearance. In such scenarios, associated NFT data is most often tied to a token via a ”tokenURI” that references the location of a JSON file containing token metadata. That file, in turn,  lives in one of these off-chain data storage protocols.\n\nTypical " + }, + { + "url": "https://ethereum.org/en/developers/docs/apis/json-rpc/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "RPC" + } + ] + }, + { + "type": "span", + "value": " providers can retrieve information about such data, then provide it to a client. A wallet client, for example, can find any media files associated with a particular NFT and display them. But this creates a trust bottleneck in the Web 3 ecosystem—requiring DApps and their users to trust the source of the data without proof. Solving this trust point is one of the primary goals of Laconic." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Today’s NFT ecosystem isn’t providing standards. Enter Laconic." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s clear that across blockchains, tokenization standards vary widely. Even with similar token types on the same chain, we see countless examples of how and where data is stored:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": " " + } + ] + }, + { + "item": "55644653", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nStay tuned for Part 2, where we’ll dive into more detail on how Laconic solves common implementation and integration challenges, offering both collectors and developers a far smoother NFT experience. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Got questions? Join us on Discord.\n" + } + ] + }, + { + "item": "55644654", + "type": "block" + } + ] + } + } + }, + "date": "2022-09-28", + "featured": false, + "id": "55644655", + "image": { + "url": "https://www.datocms-assets.com/66113/1664296930-nfts005-1.png" + }, + "slug": "99-problems-but-nfts-aint-one", + "title": "99 Problems But NFTs Ain’t One" + }, + { + "author": { + "id": "55512259", + "name": "Stefan Adolf" + }, + "category": [ + { + "slug": "insights", + "title": "Insights", + "id": "6311819" + }, + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [ + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralized apps rely heavily on blockchain state to display current and historical values like an account's balance, its NFT holdings, or current exchange rates on DEXes. Projects like The Graph or Covalent make blockchain data accessible, chain indexers like Etherscan provide historical information about the chain's state, and RPC providers like Infura or Alchemy allow wallet extensions like Metamask to connect to their full-node infrastructure. However, unless you run your very own full-node you cannot prove that any value provided by these sources truly resembles the chain state they represent. In short: relying on external, centralized services imposes a trust risk. However, the way Ethereum stores data allows trusting the chain state data without trusting the provider itself, thanks to proofs. This is how it works." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Ethereum's storage model" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ethereum uses a tree-based storage model that contains each account's balance and the storage for contract-based accounts. All values are wrapped in a traversable hash trie structure - a " + }, + { + "url": "https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Patricia Merkle Trie" + } + ] + }, + { + "type": "span", + "value": " - that allows creating cryptographic hash proofs for each tree node. The state root hash of a block represents the complete state of all accounts and contracts at a given block height, aka the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "world" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "state" + }, + { + "type": "span", + "value": ". Block producers execute new transactions on their local state copy and bundle the resulting new state root hash and a list of all executed transactions into a new block." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Light Ethereum clients cannot verify the current chain's state since they're not keeping a full copy of the world state. Full nodes can rebuild the state tree by executing every transaction since genesis, and then constantly update it as new blocks arrive. To save storage space they only keep the most recent chain states and hence cannot respond to state queries from the past, i.e. they're not able to determine an account's balance older than - depending on the node's settings - 64 blocks. That's sufficient to create proofs about the current state, though." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cryptographic proofs on hash trees" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Merkle trees use hashes of their nodes' values as keys to guarantee their content integrity. To build a simple binary Merkle tree, a prover starts by computing a hash over a node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "L" + }, + { + "type": "span", + "value": "'s content " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": ". Next, they compute the hash over " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": " and the hash of a sibling content node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "K" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": ": " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(h(K)+h(L))" + }, + { + "type": "span", + "value": ". Using this hash as a key they create a new node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "KL" + }, + { + "type": "span", + "value": " with " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(K)" + }, + { + "type": "span", + "value": " and " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": " as children and find another node" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "IJ" + }, + { + "type": "span", + "value": " at the same tree level to create a new parent node that hashes all values of the underlying tree structure: " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(IJ + KL) = h(h(h(I) + h(J)) + h(h(K) + h(L)))" + }, + { + "type": "span", + "value": ". This process is repeated until all nodes are combined to a single root hash. Since each parent node keeps a hash of its children, it's impossible to change anything down the tree without affecting the tree's root node." + } + ] + }, + { + "item": "55510966", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove that a certain base value has been incorporated into a Merkle tree's root, one collects the hashes of sibling nodes at each level of the tree. A prover successively computes their hash sums to finally recreate the root hash, thereby proving the inclusion of the node's value." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A simple way of storing binary Merkle trees in a flat key/value database structure is with " + }, + { + "url": "https://en.wikipedia.org/wiki/Radix_tree", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "radix tries" + } + ] + }, + { + "type": "span", + "value": ". To store a node, one splits its hexadecimal hash key into its nibbles (the hex characters) and create subdirectories for each of them. A node with the key " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "0xcafe" + }, + { + "type": "span", + "value": " would end up in a folder structure like " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "c/a/f/e/value" + }, + { + "type": "span", + "value": ". Looking up a node requires traversing the directory structure along the key's nibbles down to the last nibble's directory where the node's value will be stored. While simple to understand this approach is far too inefficient to store large tree data structures like Ethereum's world state. The network instead uses Patricia Merkle Tries that add some complexity to the data structure by introducing branch-, extension-, leaf- and null-nodes but are way more efficient to store and traverse. Additionally, a Recursive Length Prefix (" + }, + { + "url": "https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "RLP" + } + ] + }, + { + "type": "span", + "value": ") encoding is used to serialize nested arrays and data structures." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Proving account state" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ethereum's state trie consists of a mapping between account addresses and their state, defined by their current " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "nonce" + }, + { + "type": "span", + "value": " and " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "balance" + }, + { + "type": "span", + "value": " and in the case of contract accounts a " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "storageRoot" + }, + { + "type": "span", + "value": " key that points to a position in the dedicated contract storage trie and their binary code hash. Just using a block's " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " one can look up an account's current state at that height using a full node's LevelDB (Geth's default storage database). Our examples are using an archive node of Ethereum's recently launched " + }, + { + "url": "https://sepolia.dev/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Sepolia testnet" + } + ] + }, + { + "type": "span", + "value": " because it's still small enough to be synced to a developer's machine." + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst {\n toBuffer,\n bufferToHex,\n keccak256,\n Account,\n} = require(\"ethereumjs-util\");\nconst Web3 = require(\"web3\");\n\nconst web3 = new Web3(\"http://127.0.0.1:8545\");\nconst db = new Level(\".ethereum/sepolia/geth/chaindata\");\n\nasync function getState(address, blockNumber) {\n const block = await web3.eth.getBlock(blockNumber);\n const trie = new SecureTrie(db, toBuffer(block.stateRoot));\n\n //retrieves the account node's *value*\n const rawValue = await trie.get(toBuffer(address));\n const account = Account.fromRlpSerializedAccount(rawValue);\n console.log(account);\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "getState(\"0xe127a39da6ea2d7b1979372ae973a20bab08a80a\", 1432400)" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "value": "yields:" + } + ] + }, + { + "code": "account Account {\n nonce: ,\n balance: ,\n stateRoot: ,\n codeHash: \n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The provided account is an EOA, hence the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "codeHash" + }, + { + "type": "span", + "value": " value corresponds to " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "keccak256([])" + }, + { + "type": "span", + "value": " (another expression of a null value) and " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " is the hash of an RLP encoded zero:" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "keccak256(rlp.encode(0))" + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To create a proof of the account node's value against a block's state root you walk down the patricia trie starting at the block's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " node, resolve the paths on the trie's branch and extension nodes according to the signposts expressed by the leaf node's nibbles until the wanted leaf node is reached. On the way we're keeping track of all nodes we see while traversing the path. More details about " + }, + { + "url": "https://github.com/ethereumjs/ethereumjs-monorepo/blob/cfd7b7754490b072a035cceaba59c3dfb517effd/packages/trie/src/trie/trie.ts#L154", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the traversal can be found here" + } + ] + }, + { + "type": "span", + "value": ". We're using only high level methods to show proof creation here:" + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst { toBuffer, keccak256 } = require(\"ethereumjs-util\");\n\nconst db = new Level(\".ethereum/sepolia/geth/chaindata\");\nconst trie = new SecureTrie(db, toBuffer(block.stateRoot));\n\n//create a proof by finding the path on our own:\nconst { node: accountNode, stack } = await trie.findPath(\n keccak256(toBuffer(address))\n);\nconst proof = stack.map((stackElem) => {\n return stackElem.serialize();\n});\n\n//or by using a trie's convenience method:\nconst proof = await SecureTrie.createProof(trie, toBuffer(address));\n\n//or by using the ethereum node's RPC interface\nconst { accountProof: proof } = await web3.eth.getProof(\n address,\n [0],\n block.number\n);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All three proofs contain the same array of RLP encoded proof nodes, with our account node at its end:" + } + ] + }, + { + "code": "[\n \"0xf90211a078400dd6bd6f6cc13cdde5b707bdb6b9802fbd11809cba8aab2ee72f6920bbf3a0d88879eb6f43ea2d9c07538601e041aa46d64c87df68296a552ad12f8e5bebc0a044d40ac93c1bce308feda36a7712a38d9508087f04e612b1411bc57f80fa77a8a0b92c5fc0dcbc4a7982563c3667b940117770965e5f447578e4bde4fefcbb6d5aa0bc555b313ac90177e117dc10917581dad616770625e0956feb0df75a4be2ce63a0bd19008ae54cf1af36e59d7c92c553693dd468a0ef86c89401d01fc84aa4b751a0ee549b7ab8dd9a565ee6507bb46b9033e57b3a07541a4085763207911633367ca05652bd80f5b970a09c2090c38b13afa1772f3d97422c9a16548acdf629c35113a0a2e78f2d0da2d7218535b94bd69c4fad4425ff3bb22304d722185cf99dac2596a0a9470e771ae9cec5535b057948e68c4c38d8b8c1c2d8ed180d8f5244c63e422ea0726d10a2a5c85b156105414e75591611d896b8aaebd7fc92535845445a8baa81a0fdfb5aa2c35136eab67c3539d3ba34648bb8542b57e745976d52bca263ab19d7a01eafa4a1f59d28009a03b24d9a11c878faa78fe7ef3ecc4ef8891a77fea3fa35a02c980b0a841d5ec1978ae63adf7ba2727ac125f792f545cb0146d8df46adbdbfa052b97e4692fd69b4bebc6ceca258032319b07aca2ee62b504285031670fbc073a02c415e41b6bf838a8b742123fda110703ffdedea8834bb5113ae26f81887668e80\",\n \"0xf90211a098e0b7071d2b8d890cca479dba9f5b697d639d70d51f0312c5b8a27afc31a23da060759f93f482f66b233480a4cd060372567752b9dae507045b266600ab9b290da0c35bb5b2aae7eb8bc75a9de125daab9caa7d6d39e403153c9b34e51dcde46f78a097a4c1ba8ac42378481754eac9306b62d3d176602098820cb87e3c7f4091734da074b2785ca9199d0c68e2fa6dd01dcc24791887616adf8043cda56651cff25e87a00937963d57bc119144ebeff9bb99dcbef11aed563fd41818aaf0de89d15364b3a000ba5def8acd7c1c2af85ef49bc5d144fa1ceef93e81e9e64ad6457ab92cb569a069cd4b3ac6b03b1928f50840f81086ff0c819d1cbd445db0e5c306cfce9c0728a0a78339d0b9405da5a7943a6ac97b9585767a6f9f0e481b5e197d8fd820ac1cfaa019e1319ade8f1c2254dcef0b68483240044bc6bee2ea8197de895eb427068b1aa0ef36884df92b5769e521c6c0feaead503a8d70d11f493b3e6c8170ac917ced92a079c1ffdaa2a8d4ddacac77f47b43f1553c634300d23a8003574ec90874547cbaa0de4f130f1fd52baf5229e3871394b6f0562c64576039c6c38d06c695b7d87b07a09668798efa8c1e9b860d9f4a49b65db83c22fefd92f703a5875b87c7ae11bdf4a087ca8f145dbc9ad7f88468dbec0627d77ced63a508b4d7a5d28439e89c6be8e4a04fdb75be4fce4d76e41578f0bbcfdc2ded74d59855c9f3afc73c3659b9e28aa980\",\n \"0xf90211a058d99773cf489bd86bac2c6452c20cc7056f3505d7f96d83480cf85336d0da85a07dc4216585f73a213fb1828076cf7d93d8ce95e1981a5c8eb11d7166af8433eba0f85f31a95a1f83516719ae596266afdc529aa4ff9af8f0f6c1d7da6751f07d70a0f1371490b8ef31ce814673e9ecf0f498f2ec9eedcd554e93162d5c0e99e77d69a01d9709211cc089eba15fe3c052539203d11235b219e1c72e0172c9bec1d317bea09e907ad084233149719b5c6f8b4777a617b41744a0c3e9f6ef002970465a0656a044f3abc34546455ba2ed3d64ead41c734d5f56c240f8f8adfd971179538ca0efa08403716832691c8867b1366421b703925ff28e45d6f4360f898b70461e29d4eda0d8f4711ffe7bc79040b2f397523786a67d72814e8af9b65c9e03c318bb0eba48a0db84d2a268e67d6e45af7cd18a92fab1df496180e9ba8f0f0b620ab6344457cba0d01707094316206d689d80ed2684cda3aadd72ee2d4efbe1437bb194aa0adc87a0355e0e203341ac995bf2d90937f1ce807a13624e9d3831476f3205838b84f191a083961a9b498b791ee8e6a110a87aca2f2cea629df0827f4fbd168567d7246207a05d312338764151dc1936485637367ad9d9c440b4b0dde368dec4dbd5aaf51f4ea076b77567690221701e2edfff06f1c84a25b3181e4c8aa3bed2b3e3797a883564a0975b63f1be5615a4a68ec7f313abc0784def11a352975c40e886e03df95f764e80\",\n \"0xf9013180a0944968a4a8c7ba1e91cbb2414471a67f16c74e2ef6ac87447af0402520aed9c88080a03f3e1fed1008e242b89ce5b56106a091f946826c539f23ecee74c5b36b5d38cca041b58c41b5435e10c9b9f6b1f1bdf3d5e0294dfcfbf59b7a8c80475249e1d9e6a0d0b42862dcb175e47bf17614bd251b17cdab2710ba13175fd9b8c17afdc0893ba0ff4dc7ee613a7805802e50d39c3c8d8b35db63ceda7371502c5d119eca0a7b1780a0cd328766d44f930edc3a32ae5fd2a791f0de4dbabe014c4be77fc63b4b427310a0c20a6a1812f18d0c446408f90b09d0b592a07c45d06e780b49c6ad68e7c92bc5a084688c931305a75246f9d527a07483cff6e9ffbe4746a22ca65e07d3b3c6045f8080a0515f82b6e34b4713edead2652f266fdcea3729d64217113db564e918a9b1ed408080\",\n \"0xf8518080a06b91f274ef06ab455966416f6bb3779519bf72c68e85ec4b61ff8b99aa73a1818080a0937ee4540dc495eaa3cd61e12a4f6158a6be147d71ac3955a50ecd080e9732698080808080808080808080\",\n \"0xf8709e3e0484bbc22108bc77412e65255ce0387dc7c6a8d1917625ddb60ccbd98fb84ff84d028901f3d52b3c4f92e45da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"\n]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To verify this proof one doesn't need to have access to a full state tree at that block height. It's sufficient to know (and trust, e.g. by running a light node) the proof block's " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst { toBuffer, keccak256, Account } = require(\"ethereumjs-util\");\n\n// look Ma, no ethereum node needed\n// @param stateRoot hexString: the block's stateRoot\nasync function verifyAccountProof(address, proof, stateRoot) {\n const proofBufs = proof.map((p) => toBuffer(p));\n //build a new trie using only the proof nodes. They will hash towards the block's stateRoot\n const proofTrie = await SecureTrie.fromProof(proofBufs);\n\n //the account node can also be retrieved from the partial trie:\n const accNodeRaw = await proofTrie.get(keccak256(toBuffer(address)));\n const account = Account.fromRlpSerializedAccount(accNodeRaw);\n\n console.log(\"proven value\", account);\n const valid = await proofTrie.checkRoot(toBuffer(stateRoot));\n //or: const valid = bufferToHex(proofTrie.root) == stateRoot;\n return valid;\n}", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Proving contract state" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Proofs over contract state are created accordingly. They are rooted at the contract account's own " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " member variable that points to the mutable root of the contract's storage trie. Storage slots are addressed by their position hash as outlined in the " + }, + { + "url": "https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html#mappings-and-dynamic-arrays", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Solidity documentation" + } + ] + }, + { + "type": "span", + "value": " and are stored as leaf nodes in the storage trie. To simplify this procedure, we're going to use " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "eth_getProof" + }, + { + "type": "span", + "value": " calls here as described in " + }, + { + "url": "https://eips.ethereum.org/EIPS/eip-1186", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "EIP-1186" + } + ] + }, + { + "type": "span", + "value": " and supported by all major chain node implementations and service providers. Lets prove " + }, + { + "url": "https://sepolia.etherscan.io/address/0xF492600AeD292b1B94A1ba0CD29fB6ed6d6ab872", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this contract" + } + ] + }, + { + "type": "span", + "value": "'s " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "greeting" + }, + { + "type": "span", + "value": " to be \"" + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "Hello, Hardhat!" + }, + { + "type": "span", + "value": "\" at Sepolia block number " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "1391140 " + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "pragma solidity ^0.8.0;\n\ncontract Greeter {\n string private greeting;\n\n constructor(string memory _greeting) {\n greeting = _greeting;\n }\n\n function setGreeting(string memory _greeting) public {\n greeting = _greeting;\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now, lets create a proof of its storage slot 0 (the string variable " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "greeting" + }, + { + "type": "span", + "value": " ):" + } + ] + }, + { + "code": "const { bufferToHex } = require(\"ethereumjs-util\");\nconst { SecureTrie } = require(\"merkle-patricia-tree\");\nconst Web3 = require(\"web3\");\n\nconst web3 = new Web3(\"http://127.0.0.1:8545\");\n\nconst proveContractStorage = async (blockNumber) => {\n const proof = await web3.eth.getProof(\n \"0xf492600aed292b1b94a1ba0cd29fb6ed6d6ab872\", //the contract's address on Sepolia\n [0], //the first storage slot\n blockNumber\n );\n console.log(proof.storageProof);\n const value = web3.utils.hexToAscii(proof.storageProof[0].value);\n console.log(value);\n\n const proofBufs = proof.storageProof[0].proof.map(toBuffer);\n const proofTrie = await SecureTrie.fromProof(proofBufs);\n const valid = bufferToHex(proofTrie.root) == proof.storageHash;\n console.log(valid);\n};", + "type": "code" + }, + { + "code": "[\n {\n key: '0x0',\n value: '0x48656c6c6f2c204861726468617421000000000000000000000000000000001e',\n proof: [\n '0xf8518080a0e2a22c03c4f21673563d55cbad16de4c4affc9a54c3eea063ce358ccd6d02c4c8080808080808080a0fc47ec7aea920817d850c758a8fd61ecc89b967b2fe8d9ec6d00feedeb0a7d658080808080',\n '0xf843a0390decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563a1a048656c6c6f2c204861726468617421000000000000000000000000000000001e'\n ]\n }\n]\nHello, Hardhat!\ntrue", + "type": "code" + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "A non trivial example" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Creating proofs over nested structures, dynamic types like strings, or deeply buried storage slots is slightly harder because you have to be familiar with Solidity's " + }, + { + "url": "https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "storage layout" + } + ] + }, + { + "type": "span", + "value": ". To demonstrate that, here is a non-trivial example to prove that Jimmy Fallon owned " + }, + { + "url": "https://opensea.io/assets/ethereum/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d/599", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Bored Ape #599" + } + ] + }, + { + "type": "span", + "value": " at block height " + }, + { + "url": "https://etherscan.io/block/13572667", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "13572667" + } + ] + }, + { + "type": "span", + "value": " as he claimed during his Tonight Show " + }, + { + "url": "https://www.youtube.com/watch?v=5zi12wrh5So&t=222s", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "in January 22" + } + ] + }, + { + "type": "span", + "value": ". The Bored Apes NFT contract inherits from OpenZeppelin's legacy V3 " + }, + { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.2/contracts/token/ERC721/ERC721.sol#L36", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "enumerable ERC721 contract" + } + ] + }, + { + "type": "span", + "value": ". The data structure storing ownership information about a single token is the private " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "_tokenOwners" + }, + { + "type": "span", + "value": " member struct that maps a token id to an index onto another dynamic array of key value mappings." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove the ownership storage values we need to take a look at the contract's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "ownerOf" + }, + { + "type": "span", + "value": " implementation and its related structs. Here's a summary of the relevant parts of the " + }, + { + "url": "https://etherscan.deth.net/address/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d#code", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "full BAYC code base" + } + ] + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "library EnumerableMap {\n struct MapEntry {\n bytes32 _key;\n bytes32 _value;\n }\n\n struct Map {\n MapEntry[] _entries;\n // Position of the entry defined by a key in the `entries` array, plus 1\n // because index 0 means a key is not in the map.\n mapping (bytes32 => uint256) _indexes;\n }\n\n struct UintToAddressMap {\n Map _inner;\n }\n\n function _get(Map storage map, bytes32 key) private view returns (bytes32) {\n uint256 keyIndex = map._indexes[key];\n require(keyIndex != 0, \"EnumerableMap: nonexistent key\"); // Equivalent to contains(map, key)\n return map._entries[keyIndex - 1]._value; // All indexes are 1-based\n }\n\n function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {\n return address(uint160(uint256(_get(map._inner, bytes32(key)))));\n }\n //...\n}\n\ncontract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {\n\n // Mapping from holder address to their (enumerable) set of owned tokens\n mapping (address => EnumerableSet.UintSet) private _holderTokens;\n\n // Enumerable mapping from token ids to their owners\n EnumerableMap.UintToAddressMap private _tokenOwners;\n\n function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n return _tokenOwners.get(tokenId, \"ERC721: owner query for nonexistent token\");\n }\n //...\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The index mapping " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "_tokenOwners" + }, + { + "type": "span", + "value": " is the contract's third storage member (the 1st one being part of the ERC-165 implementation) and it implicitly points to the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "Map" + }, + { + "type": "span", + "value": " struct via " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "UintToAddressMap.inner " + }, + { + "type": "span", + "value": ", occupying two storage slots. By applying Solidity's " + }, + { + "url": "https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "layout rules for dynamic arrays" + } + ] + }, + { + "type": "span", + "value": ", the storage slot's address of the enumeration index that points to the current owner of token #599 can be computed as " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "const indexSlot = web3.utils.soliditySha3(599, 3);" + }, + { + "type": "span", + "value": ". Querying that storage slot's value by " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "web3.eth.getStorageAt(baycAddress, indexSlot, blockNumber)" + }, + { + "type": "span", + "value": " yields the index " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "0x258" + }, + { + "type": "span", + "value": " at the given block height. Considering that the " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "Map._entries" + }, + { + "type": "span", + "value": " array starts at the contract's third slot, it takes two slots to store one " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "MapEntry" + }, + { + "type": "span", + "value": " and the indexes are 1-based we can resolve the map's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "_value" + }, + { + "type": "span", + "value": " member that carries the token owner's address like so: " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "code" + ], + "value": "soliditySha3(2) + (0x258 * 2) - 2 + 1" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With that we can construct two storage proofs for the index and the owner's address:" + } + ] + }, + { + "code": "const baycAddress = \"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d\";\nconst blockNumber = 13572667;\n\nconst indexSlot = web3.utils.soliditySha3(599, 3);\n// -> 0x7e2616eb7a75f68a32624f502cf2cabc166c302900bbdc790c2fb85cea316a21\nconst indexValue = await web3.eth.getStorageAt(\n baycAddress,\n indexSlot,\n blockNumber\n); //0x258\n\nconst bnIndex = ethers.BigNumber.from(indexValue);\nconst valueSlot = ethers.BigNumber.from(web3.utils.soliditySha3(2))\n .add(bnIndex.mul(2))\n .sub(2)\n .add(1);\n// -> 0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5f7d\n\nconst proof = await web3.eth.getProof(\n baycAddress,\n [indexSlot, valueSlot],\n blockNumber\n);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove the " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " of the yielded proof, we can:" + } + ] + }, + { + "code": "async function validateStorage(proof, slot, proofIdx) {\n const proofBufs = proof.storageProof[proofIdx].proof.map(toBuffer);\n const pTrie = await SecureTrie.fromProof(proofBufs);\n const valid = pTrie.checkRoot(toBuffer(proof.storageHash));\n const rlpNode = await pTrie.get(toBuffer(web3.utils.keccak256(slot)));\n console.log(\"content at slot\", slot, bufferToHex(rlp.decode(rlpNode)));\n return valid;\n}\n\nconsole.log(await validateStorage(proof, indexSlot, 0));\nconsole.log(await validateStorage(proof, valueSlot.toHexString(), 1));", + "type": "code" + }, + { + "code": "content at slot 0x9f82913e56c1ea296cd5a3c46bc89a4073098f41767359e4c3742445923985c7 0x0258\ntrue\ncontent at slot 0xd4790f3899b463e8194660196b97cf3ff9c47008d83ca7c0e51ce406d3c784e5 \\\n0x0394451c1238cec1e825229e692aa9e428c107d8 (<- Jimmy Fallon's address)\ntrue", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Validating proofs inside smart contracts" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These have been client side examples, but proofs can also be validated inside Solidity contracts, e.g. to prove historical chain information that's not available to the contract itself. A good example can be seen at Lido Finance's " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "trustless ETH/stETH price pool oracles" + } + ] + }, + { + "type": "span", + "value": " that receives price reports as a combination of block header, account and state proofs. Since proofs are submitted " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle/blob/main/contracts/StableSwapStateOracle.sol#L295", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "as memory variables" + } + ] + }, + { + "type": "span", + "value": ", price updates " + }, + { + "url": "https://etherscan.io/tx/0xb24e20813e08f75e12e29da53f7f6c7e5dca7be68f3d3247edd4de572c527df4", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "are relatively cheap" + } + ] + }, + { + "type": "span", + "value": ". Here's a contract that's built on " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle/tree/main/contracts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Lido's primitives" + } + ] + }, + { + "type": "span", + "value": " and verifies any given state proof. It's also " + }, + { + "url": "https://goerli.etherscan.io/address/0x52e357f616a13089435be73e20cffb788eb4c928#code", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "deployed on Görli" + } + ] + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\n\nimport \"./StateProofVerifier.sol\";\nimport {RLPReader} from \"solidity-rlp/contracts/RLPReader.sol\";\n\ncontract ProofVerifier {\n using RLPReader for bytes;\n using RLPReader for RLPReader.RLPItem;\n using StateProofVerifier for StateProofVerifier.Account;\n\n /*\n struct Account {\n bool exists;\n uint256 nonce;\n uint256 balance;\n bytes32 storageRoot;\n bytes32 codeHash;\n }\n\n struct SlotValue {\n bool exists;\n uint256 value;\n }\n */\n\n function extractAccountFromProof(\n address _address,\n bytes32 _stateRootHash,\n bytes memory _proofRlpBytes\n ) public pure returns (StateProofVerifier.Account memory account) {\n RLPReader.RLPItem[] memory proofs = _proofRlpBytes.toRlpItem().toList();\n bytes32 addressHash = keccak256(abi.encodePacked(_address));\n\n account = StateProofVerifier.extractAccountFromProof(\n addressHash,\n _stateRootHash,\n proofs\n );\n }\n\n function extractSlotValueFromProof(\n bytes32 _slotHash,\n bytes32 _storageRootHash,\n bytes memory _proofRlpBytes\n ) public pure returns (StateProofVerifier.SlotValue memory slotValue) {\n RLPReader.RLPItem[] memory proofs = _proofRlpBytes.toRlpItem().toList();\n slotValue = StateProofVerifier.extractSlotValueFromProof(\n _slotHash,\n _storageRootHash,\n proofs\n );\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Continuing with the proof of Jimmy Fallon's Bored Ape, this is how you would prepare RLP encoded versions of the proof's node array as required by the contract's method interface:" + } + ] + }, + { + "code": "//converts an array of rlp encoded proofs to an rlp encoded array of proofs.\nconst rlpEncodeProof = (proof) => {\n const rlpDecodedProofs = proof.map((p) => rlp.decode(toBuffer(p)));\n return rlp.encode(rlpDecodedProofs);\n};\n\nconst rlpAccountProof = rlpEncodeProof(proof.accountProof);\nconst indexStorageProof = rlpEncodeProof(proof.storageProof[0].proof);\nconst valueStorageProof = rlpEncodeProof(proof.storageProof[1].proof);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and submit it to the contract:" + } + ] + }, + { + "code": "//state root of mainnet block #13572667\nconst blockStateRoot =\n \"0xa710dad6c716e0b762a671865cbe0d286f158198580f4ac97c4ace95ea85ba1b\";\nconst baycAddress = \"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d\";\n\nasync function main() {\n const Verifier = await ethers.getContractFactory(\"ProofVerifier\");\n // 0x52E357F616a13089435bE73E20CFfB788Eb4C928 on Görli:\n const verifier = Verifier.attach(process.env.CONTRACT_VERIFIER);\n\n //\"account\" is the bored apes contract\n const accountResult = await verifier.extractAccountFromProof(\n baycAddress,\n blockStateRoot,\n rlpAccountProof\n );\n const indexResult = await verifier.extractSlotValueFromProof(\n ethers.utils.keccak256(indexSlot),\n accountResult.storageRoot,\n rlpIndexProof\n );\n const valueResult = await verifier.extractSlotValueFromProof(\n ethers.utils.keccak256(valueSlot),\n accountResult.storageRoot,\n rlpValueProof\n );\n\n console.log(\n accountResult,\n indexResult.value.toHexString(),\n valueResult.value.toHexString()\n );\n}", + "type": "code" + }, + { + "code": "[\n exists: true,\n nonce: BigNumber { value: \"1\" },\n balance: BigNumber { value: \"0\" },\n storageRoot: '0x3f99b7df7989c11417c18b517c333ec74104e23ae76a50d578292ba3d466d77d',\n codeHash: '0x0ba5e25e74d81bab327110c8d8b44320f50ad5c3e91a546a5c5a9b605cf653b3'\n]\n0x0258\n0x0394451c1238cec1e825229e692aa9e428c107d8 // <- Jimmy Fallon, again.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Creating " + }, + { + "url": "https://eips.ethereum.org/EIPS/eip-1186", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "EIP-1186" + } + ] + }, + { + "type": "span", + "value": " compatible historical cryptographic proofs as demonstrated is a rather demanding task. It requires provers to traverse their archive nodes' LevelDBs, requiring up to 16 disk operations per account and storage slot. However, since state proofs like Jimmy Fallon's Bored Ape ownership at block height 13572667 are deterministic and valid forever, one could presciently collect all of the hashes along the nodes from root of the state tree down to specific nodes and index them. Chain indexers or RPC relayers like Metamask could use those hashes to execute proofs, thus minimizing trust in the service itself: each reply would be provable by the client." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network greatly simplifies the process of generating proofs for blockchain data, saving time for developers, and avoiding the extensive recursive querying of full-node databases that would otherwise be required." + } + ] + } + ] + } + } + }, + "date": "2022-09-20", + "featured": true, + "id": "55510965", + "image": { + "url": "https://www.datocms-assets.com/66113/1663659141-laconic_proof_blog_2.png" + }, + "slug": "what-is-a-proof", + "title": "What Is a Proof and Why Do You Need One?" + }, + { + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "category": [ + { + "slug": "insights", + "title": "Insights", + "id": "6311819" + }, + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick Dudley, the co-founder of " + }, + { + "url": "https://laconic.com", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the Laconic Network" + } + ] + }, + { + "type": "span", + "value": ", talks to Chjango Unchained on a special live-streamed episode of " + }, + { + "url": "https://www.youtube.com/watch?v=viE0BZGx_DU", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Interchain.FM" + } + ] + }, + { + "type": "span", + "value": " about the risks of monopolization, why DApps have a hard time querying Ethereum data, how the Laconic Network solves this problem, and the legal aspects of creating a truly decentralized DApp data marketplace." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "How Laconic preserves verifiability" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is a data indexing service with an Ethereum L2 rollup that uses the Cosmos SDK and a fork of Ethermint. “Primarily, what we're doing is making third-party verifiable caches and indexes of Ethereum data, and we're building a marketplace to facilitate the buying and selling of that data.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For Ethereum-based apps, developers only need a subset of the Ethereum state to run their DApp. Laconic connects you with service providers who can serve that data to DApps, all in a decentralized, disintermediated way." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike other DApp data providers, Laconic focuses on the verifiability of the provided data. Rick points out that “there is no other platform that is preserving the verifiability in the way that we are.” Achieving this verifiability is anything but easy. If a DApp wants to run verifications using an archive node, it would take months of moderate computing time to do that, not to mention archival storage. “Most people don't have 14 terabytes just laying around that they can attach to their phone or laptop.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s answer is to split the data and decentralize validation. As Rick explains, “we take the whole Ethereum blockchain. And then we get a bunch of validators together and make you a little mini proof-of-stake blockchain of just the information you care about.” Ethereum remains the L1 in this scenario; “all the value that's transacted on the Laconic Network is actually staked or escrowed on Ethereum.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Leveraging linked data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic solution builds upon an established data format (IPLD, or Interplanetary Linked Data) to deliver the promise of verifiable data. The IPLD format not only allows transforming data to better serve the needs of DApps, but also makes it possible to verify these transformations. “It's not magic, but it makes it easier for us to deal with these very complex and robust data types. It also allows us to do cross-chain proofs, which is the main appeal.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only cross-chain proofs, but proofs about Ethereum events—which is not possible in Ethereum directly. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "“Events were never intended to be provable there. They weren't supposed to be used in the way they're used today.”  Rick explains how difficult it is for the average DApp developer to know that a single event really came from Ethereum, “You have to have the hundreds or thousands of events that came with that event, or you have to rerun the transaction in the block and see that it emitted the event. And both of those things are extremely expensive.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic has a technique for saving the effort of doing that re-computation, and IPLD plays a large part in that." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Acting lawfully" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network will have up to 67 validators that will be located in different countries. This raises some legal questions. Chjango points to a scenario where sanctions against countries can break the promise of decentralization. Rick explains how Laconic’s legal structure addresses this issue. Laconic is run by an LLC that is designed to “comply with the laws within its jurisdiction and to allow each member to comply with the laws within their jurisdiction.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If a country imposes sanctions on another country, the members are free to follow their local law and refuse service to members of the sanctioned countries. At the same time, the rest of the Laconic Network remains available for those accounts. All this can be done without sacrificing privacy. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "“You can use encryption and other things for someone to say ‘is this user in the United States, yes or no,’ without revealing where the user actually is.” Rick concludes that, “of the 67 members, we hope to have members that are willing to and legally able to service customers in those jurisdictions that are currently underserved.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Blockchain decentralization, or the lack thereof" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization in conjunction with global distribution is key to providing uninterrupted service in the face of ill-intended governments or political disturbance. Rick considers decentralization to be retreating, giving way to new monopolies. In other words, there is a re-centralizing effect." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The problem starts with the cost of verification. Chjango asserts that “the difficulty of fully verifying an Ethereum blockchain is such that it has hurt its decentralization, even though the underlying blockchain is decentralized.” The expense of running a fully validating node has introduced new centralization choke points." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick places the problem one level down, at the network. The internet comprises 60,000 sub-networks that communicate over the Border Gate Protocol (BCP). But a traffic analysis found Ethereum network traffic on only 18 subnets. And a handful of companies – AWS, Google, Microsoft, Cloudflare –  run much of the infrastructure, and are also in the same legal jurisdiction. So a single company can switch off large parts of the Ethereum system." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This lack of decentralization continues at the blockchain level. “There are 4000 validating nodes on the network, but about 80% of them are in AWS. And somehow, in spite of that, 50% of the blocks still come from three people.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic's answer to this problem is “forced decentralization.” Rick stated, ”Laconic requires its members to run their own hardware and to be located in different data centers. And we may at some point require people to run their own networks on the internet, so that we do maintain decentralization at that level.” Here, the legal side plays an important role.  The LLC can positively assert that a Member is a specific legally registered entity. They signed a contract that stipulates they avoid being in the same data center as another Member, or else they're in breach of the contract and they risk being removed from the network. \"That's a big benefit of the LLC, and definitely something that we've spent time thinking about to design it right,\" Rick continued." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Monopolistic forces are unavoidable. Give people an option to exit a toxic monopoly" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Even with Laconic’s solution, scalability and sustainability of the whole blockchain ecosystem are not a given. As pointed out earlier, more than 50% of Ethereum blocks are created by only three mining pools." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "(Source: " + }, + { + "url": "https://etherscan.io/stat/miner?blocktype=blocks", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Etherscan.io" + } + ] + }, + { + "type": "span", + "value": ")" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Such monopolization tendencies are hard to fight. Rick’s view is to initially accept the inevitable. “I think it's more about acknowledging that these things are going to be monopolized. And then building technology that allows us to exit those monopolies as needed, more than trying to build technology that's going to resist the monopoly.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The aim of Laconic is to make it trivial for people to exit when those systems get monopolized. To achieve this, Laconic has a few layers of protection in place to stop the Laconic Network from being compromised by monopolization attempts. As an example, the legal agreements prevent Laconic members from sharing investors. So an investor cannot simply invest in over one-third of the members to take it over." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But if some sort of monopolization does occur, every DApp user has an option to exit. “If every user of a DApp has their own Watcher (Laconic’s API servers) running, they would have all the state they needed to move that DApp to another chain. They could take that one Watcher, generate a new state snapshot for that application, and then move that state snapshot to a new chain. And that literally would take a year of processing on Ethereum." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What’s in the name Laconic?" + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Why “Laconic”? " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick explains, “It means terse.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Referencing the ancient inhabitants of Laconia known for their more concise and terse style of speech, Laconic also describes a style of speaking or writing that uses only a few words, often to express complex thoughts and ideas." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s “laconic” technology  elegantly simplifies the complexity of blockchain, and accelerates DApp development." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Takeaways" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This was a wide-ranging interview with deep-dives on many topics not covered here. Dive in to find out more about:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Payment channels and validator accountability." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Processing time and why they don’t discuss network speed or TPS at Laconic." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Why Laconic chose Geth over other Ethereum clients." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Catch the full interview on " + }, + { + "url": "https://www.youtube.com/watch?v=viE0BZGx_DU", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "YouTube" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thanks to " + }, + { + "url": "https://www.youtube.com/channel/UCMcYl850ni93ZDqchYF7v1w", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Chango Unchained" + } + ] + }, + { + "type": "span", + "value": " and Interchain.FM for leading this thought-provoking interview with Rick. It’s exciting to be able to showcase the work happening at Laconic, designing and implementing a solution for truly decentralized delivery of provable data from the Ethereum blockchain." + } + ] + } + ] + } + } + }, + "date": "2022-09-13", + "featured": false, + "id": "55457818", + "image": { + "url": "https://www.datocms-assets.com/66113/1663008114-chjango.png" + }, + "slug": "rick-dudley-on-interchain-fm", + "title": "Rick Dudley Discusses Laconic Network on Interchain.fm" + }, + { + "author": { + "id": "24856058", + "name": "Maly Ly" + }, + "category": [ + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Watchers in the Laconic Network are the keystone technology that will scale blockchain data access for the next wave of Web3 adoption. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic stack provides a way for DApp developers to retrieve the blockchain data they require in a manner that is efficient, verifiable, and decentralized. The mechanism by which the relevant subset of blockchain data is queried, cached, and delivered, is the Laconic Watcher." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve previously explored " + }, + { + "url": "https://www.laconic.com/blog/how-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "how the Laconic Network uses IPLD" + } + ] + }, + { + "type": "span", + "value": " (“the data model of the content-addressable web”) to keep off-chain data cryptographically verifiable across transformations. The Laconic Watcher is the component that makes that data available for DApps.  You will learn about the role of Watchers within the Laconic Network, how they work, and what you can use a Watcher for." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "DApp Developer Challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A public, permissionless blockchain is optimized for storing large amounts of data and achieving consensus. A DApp typically needs only a tiny fraction of that data for its operations. Ethereum clients are not designed to serve the data needs of a given DApp, such as reading and linking data from multiple contracts, introducing cross-chain data, or presenting DApp-specific information to users. Existing options for retrieving blockchain data have some drawbacks:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Significant effort required: " + }, + { + "type": "span", + "value": "Running an archive node gives access to all of a blockchain’s data.  Structurally, blockchains are write rather than read optimized. Many developers are unable to, unwilling to, or cost-prohibited from, running the infrastructure." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Trustlessness broken: " + }, + { + "type": "span", + "value": "Centralized services can provide indexed blockchain data, but  they lack the mechanisms to cryptographically prove their accuracy and provenance. DApp developers and users must trust these services, contrary to the decentralized and trustless promises of Web3." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our goal is to make DApp development as frictionless as possible. When you build your DApps, Laconic Watchers relieve you of the infrastructure burden and enable you to query and receive trustless, verifiable, off-chain blockchain data." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Watcher and the Laconic Network" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To understand the Watcher’s role and functionality, let's briefly recap how the Laconic solution works." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The primary function of the Laconic Network indexing is to keep an up-to-date version of blockchain data in a relational database. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To achieve this, a Laconic Full Index Node fetches the latest blockchain updates and turns them into IPLD blocks with advanced indexing. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike existing ETL (Extract-Transform-Load) solutions that convert blockchain data into a searchable database format, the Laconic Network continuously monitors and evaluates state diffs on the chain, retrieving and indexing relevant tries." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This additional data opens up a new range of opportunities for DApps. This is the first component of the Laconic solution.  However, the database is still too large and too general for DApps to use; raw blockchain data structures are not suitable for DApps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Enter the Laconic Watcher." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Your DApp’s Personal Data Delivery Service" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Watchers are the component that makes DApp development as frictionless as possible. Watchers serve three fundamental purposes:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Query" + }, + { + "type": "span", + "value": " the Laconic Full Index Node for the specific data your DApp needs." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Transform" + }, + { + "type": "span", + "value": " the queried data to make it consumable for your DApp." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cache" + }, + { + "type": "span", + "value": " the data for fast and inexpensive access." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Watchers run queries relevant to your DApp through a GraphQL interface; you can create precisely the API you need, transforming the data as required. Watchers run as daemons, constantly updating the cache with transactions from the blockchain's head." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your Watcher is effectively your personal, virtual, read-only blockchain, containing the specific data your DApp needs. You get the same verifiability you would from the source blockchain, without the same burden or overhead." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Where do I find Watchers? How can I write one? Do I have to?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Running Watchers: " + }, + { + "type": "span", + "value": "It is the job of Service Providers on the Laconic Network to run Watchers. One or more Service Providers can run a given Watcher." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Do I have to write my own Watcher?" + }, + { + "type": "span", + "value": " As a DApp developer, you don’t necessarily need to create your own Watcher. Some alternatives:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Pay to access a public Watcher API. " + }, + { + "type": "span", + "value": "The fees are split between the Service Provider and the Watcher creator." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Check the Watcher Registry:" + }, + { + "type": "span", + "value": " If a Watcher’s creator chooses to make it publically available, they can add the Watcher to the on-chain Watcher Registry for easy discovery. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "You don’t have to start from scratch. " + }, + { + "type": "span", + "value": "Watchers are composable. You can build a Watcher on top of one or more existing Watchers found in the Watcher Registry." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What about Smart Contracts? " + }, + { + "type": "span", + "value": "As a Smart Contract author, you can auto-generate Watcher code directly from your Smart Contract's Solidity source code. The code generator is capable of generating the Watcher GraphQL API based on eth_calls as well as storage variables in the Smart Contract." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Watchers, in Conclusion" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Watchers" + }, + { + "type": "span", + "value": " allow DApps to cache and query verifiable blockchain data served by a decentralized network of Service Providers. Watchers facilitate the creation of custom APIs suitable for a DApp’s specific needs. " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "As a developer," + }, + { + "type": "span", + "value": " you will benefit from the Laconic Network by finding, writing, or combining Watchers to serve the backend data needs of your DApps." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "As a Laconic Network Service Provider or Validator," + }, + { + "type": "span", + "value": " you will be part of solving the problems of blockchain data access for the next wave of Web3 adoption. " + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stay up-to-date with Laconic News" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Join our " + }, + { + "url": "https://discord.com/invite/ukhbBemyxY", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Discord server" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Subscribe to our " + }, + { + "url": "https://t.me/share/url?url=https%3A%2F%2Fwww.laconic.com%2Fblog%2Fhow-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Telegram channel" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Sign up for our mailing list below" + } + ] + } + ] + } + } + }, + "date": "2022-09-06", + "featured": true, + "id": "55448692", + "image": { + "url": "https://www.datocms-assets.com/66113/1662426120-laconic-watchers.png" + }, + "slug": "laconic-watchers", + "title": "Laconic Watchers: Ensuring Trustlessness in Web3" + }, + { + "author": { + "id": "7023106", + "name": "Christoph Berger" + }, + "category": [ + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [ + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Accessing blockchain data off-chain is no longer a matter of trust" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The mission of the Laconic Network is to ensure that decentralized off-chain caches can serve blockchain data without losing the ability to prove the integrity of that data across data transformations. This post shows how the Laconic Stack maintains the verifiability of Ethereum data for its decentralized caching and querying solution. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Querying blockchain data is difficult and expensive" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "DApp developers face a dilemma when it comes to working with blockchain data. Ethereum data is stored in various ways, including the state, storage, and transaction tries, as well as in event logs. The original concept for accessing Ethereum data was that DApps would run or have access to an Ethereum full node and query it directly. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "However, DApp developers rarely want to run their own full node only to gain access to the data they need. Further, and more problematic, due to the way data is stored on Ethereum, there is often no direct way of querying for particular information. Because blockchains are optimized for writing new blocks and achieving consensus, they cannot be indexed like a classic database. As a result, to query for data, developers have to make multiple calls to the Ethereum API, replay transactions to read the events emitted, and store intermittent state in the application to join it with data from subsequent invocations.  This process is difficult and expensive." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The alternative to running a full node is to use a middleware indexing and caching service. While this alternative simplifies data acquisition for developers, it introduces some new and significant problems. In addition to high costs, long setup times, reliability issues, and the dependence on Web2-style centralized services, there is one problem that is especially pernicious: the data served by these services must be " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "trusted" + }, + { + "type": "span", + "value": ". It is currently impossible for existing centralized data providers to offer cryptographic proofs for every byte of data served." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Solution: Laconic Network and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Many people are familiar with the Interplanetary File System (IPFS), but fewer people know about the Interplanetary Linked Data (IPLD) protocol. IPLD is the underlying technology of IPFS, and it is the superpower technology behind many of IPFS’s advantages. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD can represent arbitrary data in self-describing structured objects, and it can link those objects into Merkle DAGs. As such, IPLD offers the following advantages:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is content-addressable via a Content ID (CID), just as IPFS files are content-addressable." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is cryptographically provable, meaning you can verify that a set of data belongs in a larger data set even without needing to have either set on hand. You only need the cryptographic hashes." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data undergoes natural deduplication, reducing storage and transport costs." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is tamper-proof. Any attempts to change the data that you have requested via content addressing can be foiled by validating the hashes." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD codecs map data from its original form to the IPLD data model. So if a codec exists for that data, the data has a formal representation in the IPLD data model. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Connection to Ethereum" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the case of Ethereum data, the " + }, + { + "url": "https://ipld.io/specs/codecs/dag-eth/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "DAG-ETH codecs" + } + ] + }, + { + "type": "span", + "value": " can be used to map on-chain data to off-chain IPLD structures. This mapping to IPLD makes it possible to inspect, process, or reason about data in a uniform way. The codecs cover block headers, uncles, transactions, transaction receipts, and receipt logs, as well as all the different Merkle Patricia tries that are rooted in an Ethereum block. As a result, IPLD can accurately represent any Ethereum data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike the original data, the off-chain IPLD representation enables indexing for fast querying. The restrictions of the blockchain do not apply here. Indexes can be added that allow developers to query data in ways that are otherwise difficult to achieve with the native ETH JSON RPC API. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "More importantly, the IPLD representation of blockchain data can be kept verifiable even after transforming the data into other models. Transformations are necessary to support complex DApps that aggregate and link data in app-specific ways or rely on a non-native representation of the data. The classic methods of slicing, dicing, and recombining the data would render it unverifiable. With Laconic Network data, thanks to our use of IPLD, the data remains as verifiable as it is on the blockchain." + } + ] + }, + { + "item": "55419640", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Merkleized data is provable data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The IPLD representation of an Ethereum block includes the block header and the uncles, transactions, receipts, logs, receipt trie, transaction trie, state trie, and storage tries that Ethereum stores alongside the block header. A modified Merkle Patricia Trie (MMPT), which is relevant to Ethereum, has branch nodes, extension nodes, and leaf nodes. Non-leaf nodes store the content hashes of their child nodes. This hashing goes all the way up to the root node. Therefore, the hash value in the tree root represents all leaf data. If any part of the data in a Merkle tree is tampered with, the root hash would change and differ from the root hash stored in the block header. In other words, data like state, transactions, and more, is connected to the block header in a provable way.  " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Storing all of the IPLD: Laconic Full Index Nodes" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Traditionally, Ethereum Full Nodes are used to power DApps that need to access and query all of the data of the blockchain. The disadvantages of relying on a normal Full Node are well known: " + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Expensive to operate" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not optimized for data querying" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Logs stored from events have to be replayed to access the data" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Full Index Nodes (LFIN) overcome these limitations by generating additional derivative data based on that which is stored in the Ethereum Full Node, adding new indexes, relational mappings, and materialized views, and even merkleizing some data that was previously not merkleized. The result is an amount of data that far exceeds what is normally stored. The role of the LFIN is to track and store the complete data, including derived indexes and Merkle trees of that data, in IPLD block format, for eventual consumption by DApps. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data representation inside of the LFIN optimizes many classes of queries by removing the need  to chain together data from multiple transactions. For example, listing all transactions for a particular wallet address could be achieved with a single query. Or gathering all of the node hashes needed to generate a cryptographic proof. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Laconic Watchers - interoperable and provable blockchain data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because the volume of data in the Laconic Full Index Node is so vast, most DApps would not want to have to query that database directly. Instead, DApps work with a specialized caching layer that exists to transform LFIN data into the format needed by specific DApps. These are Laconic Watchers. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data in the LFIN is still not ready for DApp developers at this point. Further transformation is needed to structure the data in the format required by your DApp. This is the role of the Laconic Watcher. Watchers will be the topic of a subsequent article, but within the scope of this article, you can understand them as being custom secondary or tertiary caches of data that directly fulfill the needs of specific applications. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thanks to our use of IPLD, these transformations can occur without losing the cryptographic integrity of the data. Developers can build their Watchers to track, process, and cache the underlying blockchain data, and then query the watchers using GraphQL. The returned data includes the linked hash structure that can be followed all the way back to the underlying blockchain. In the Laconic Network, when any data is being transformed, the hash of the inputs to the transformation and the hash of the code that performs the transformation are preserved. Thus:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "code" + ], + "value": "T(a+b) => c" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The new model, c, will contain content-hash references to a, b, and the code performing T." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One intentional consequence of our adoption of structured IPLD models for data representation is that it greatly simplifies dealing with cross chain data. Our ability to transform data while maintaining linked hashing will be invaluable when we begin to index chains beyond Ethereum. Laconic Network is designed with blockchain interoperability in mind." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The source of proof" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network improves the Web3/blockchain ecosystem greatly by combining the validation-preserving nature of IPLD with a new way of caching and transforming blockchain data in a decentralized manner. This ability frees DApp developers from relying on centralized, trust-based data providers and closes the cryptographic provability gap between the blockchain and DApps built to use blockchain data. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Stay up-to-date with Laconic news:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://discord.com/invite/ukhbBemyxY", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Join our Discord server" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://t.me/share/url?url=https%3A%2F%2Fwww.laconic.com%2Fblog%2Fhow-laconic-network-uses-ipld", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Subscribe to our Telegram channel" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Sign up for our mailing list below" + } + ] + } + ] + } + ] + } + ] + } + } + }, + "date": "2022-08-30", + "featured": true, + "id": "43258780", + "image": { + "url": "https://www.datocms-assets.com/66113/1661833374-ipld-blog.png" + }, + "slug": "how-laconic-network-uses-ipld", + "title": "How Laconic Network Uses IPLD" + }, + { + "author": { + "id": "24856058", + "name": "Maly Ly" + }, + "category": [ + { + "slug": "news", + "title": "News", + "id": "2965426" + }, + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The advent of smart-contract blockchains, led by Ethereum in 2014, has given rise to exciting new possibilities for humans interacting and transacting with each other. The ensuing rise of Web3, with wider consumer adoption of DeFi and NFTs in particular, has begun to show the world how public blockchains that are permissionless and trustless can bring immense value to industries and individuals. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The value of blockchain:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Immutable data that brings transparency to finance and governance" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Efficiency through smart contract automation " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Security and privacy through emerging cryptography " + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Yet all of these benefits hinge on developers succeeding in writing Web3 applications on top of blockchains that are fast, efficient, secure, and frictionless to use. There will be no internet-scale adoption of Web3 if the user experience is bad, or if it is simply too difficult to write the applications in the first place. And this point highlights a critical weakness of blockchains - accessing the data that they store is not as simple and straightforward as one might expect. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the contrary, since blockchains are write-optimized databases that focus nearly exclusively on establishing consensus and finality of each new block, they are notoriously difficult to work with for data retrieval." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The result of this has been the rise of a middleware industry to index, cache, and serve blockchain data. This includes services like Infura, Alchemy, and The Graph. These services, while more convenient for developers to use when building DApps, all break the fundamental benefits of blockchain in one or more ways. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, they tend towards centralization, and we end up with an industry where the blockchains are decentralized, but the DApps built on them all pass through an oligarchy of service providers that are not materially different from the IaaS cloud providers that represent the hyper-centralization of Web2. Second, they serve data that can no longer be cryptographically proven to be the correct data, so the trustless benefit of blockchain technology is replaced with a trust relationship with a single company. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Until Laconic, there has not been any technology that serves blockchain data while truly upholding the ideals of decentralization and allowing all data to be independently cryptographically verified. Key components of the emerging Web3 ecosystem, like Metamask, use centralized APIs from OpenSea and Infura without any mechanism for cryptographically verifying the accuracy or provenance of the data. Yet, provable data and decentralization are essential to having truly trustless systems for Web3 to depend on." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It is precisely this service that Laconic has been created to deliver." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Blockchain Data for High-Performance Applications" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network solves these two fundamental and existential problems. We are creating a truly decentralized network of data indexers and API providers to deliver hash-linked (cryptographically provable) blockchain data off-chain. Every byte of data served to DApps via the Laconic Network can be verified and proven to be mathematically integral." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We are thrilled at the prospect that the Laconic Network will become the catalyst and enabler to unlock immense potential value in the blockchain. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the last five years, our team of platform experts across Ethereum, IPLD / IPFS, and Cosmos SDK has been leading research and development in this space. We are creating a new set of technologies and infrastructure to solve the fundamental problem of making blockchain data accessible to high performance, high availability applications. The work has included the application of IPLD block structures to preserve the hash-linked structure of blockchain data when stored in traditional relational databases and served via API endpoints." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "The upshot: No longer will DApp developers have to struggle with convoluted queries, poor performance, long indexing times, or unprovable data when building decentralized applications. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Enter the Laconic Network" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Furthermore, we’ve pioneered a novel organizational and governance structure that solves the seemingly incompatible goals of providing guaranteed data availability service levels via a truly decentralized network. Laconic Network Members are corporate entities who enter contractual obligations to offer data indexing and retrieval services to end-users with professional-grade performance guarantees. Yet the network of these partners is technically, legally, and even geographically decentralized, and therefore impervious to censorship or monopolistic control. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the primary responsibilities of Laconic Network Members is to run Laconic Watchers, which are programs that expose data APIs to DApps, maintaining specific views of blockchain state and data expressed as mini-blockchains, solely for the purpose of reliable, provable data querying. DApp developers can find and use existing Laconic Watchers and can programmatically create new Watchers. There will be token incentives for programmers who write Watchers. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, the Laconic Network features an incentivized data caching layer to ensure robustness.  All blockchain data can be made permanently, globally available through the caching layer, without needing to directly query blockchain nodes, and, of course, without sacrificing the cryptographic integrity of the data. People who care about decentralization and provable data will be able to participate in this caching layer and earn tokens while doing it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The upcoming launch of the Laconic Network will bring a new era marked by the positive value that blockchains bring to humanity, with ever more practical and impactful use cases emerging as the barriers of data availability fall away." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network will have opportunities and benefits for anyone who cares about the future of the decentralized web:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://discord.com/invite/ukhbBemyxY", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Join our Discord" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://t.me/laconicnetwork", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Chat with us on Telegram" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and " + }, + { + "url": "https://www.laconic.com/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "sign up for our Newsletter" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "How have others tried to solve these challenges?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The original vision of Ethereum was that DApp developers would connect directly with Ethereum nodes and use the JSON-RPC API of an Ethereum client to query data. This allows you to get data from a source that has verified the data, and is decentralized, but it requires you to run the node yourself, or hire someone to run it for you. Furthermore, the data is not indexed in such a way that is convenient for DApp developers, and a large number of queries and processing must be done to satisfy normal application cases." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To improve the convenience of querying Ethereum data, many indexers have taken the data and put it in off-chain databases, often with sensible indexes. They then expose this data over new APIs that foster rapid application development and developer ease, but completely forsake the first principle values of decentralization and cryptographic verifiability. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To our knowledge, no indexer has managed to provide the convenience of custom indexes and APIs while preserving decentralization and cryptographic proofs of the data integrity." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Laconic's Solution: How are we different?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Developed over four years by our team of Ethereum, IPLD, and Cosmos SDK core contributors, the Laconic Solution encompasses three main components:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Stack, a set of services which convert Ethereum data into IPLD blocks and populate our high-performance, carefully indexed databases.\n\n" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic SDK, which makes it easy for developers to write front ends and other services which consume IPLD-based data structures and proofs.\n\n" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network, a multi-sided marketplace that allows DApp developers and service providers to transact in a more cost-effective way and then pass those cost savings on to end-users, providing a sustainable and decentralized operating model for DApp developers. The key element of the Network is Watchers, custom materialized views of blockchain data that developers query to access specific blockchain data." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Stack consists of our modified Geth client which extracts and denormalizes the data, transforming it into IPLD blocks and indexing it properly for efficient querying and future transformations. For example, we can list all the node hashes needed to execute a cryptographic proof in one query, instead of having to walk down the Merkle tree with a chain of sequential queries. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The main contact point for developers is the Watcher, a materialized view of a specific subset of blockchain data (e.g. a specific contract or protocol) that provides a custom endpoint for DApps to query.  " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "$LNT, the Laconic Network Token" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With a fixed total supply, Laconic Network Token (LNT) serves multiple functions in the network design, most saliently by securing incentive alignment across network stakeholders. In addition to being used as rewards for service providers to run Watchers, LNT also allows for seamless transactions across services, regardless of blockchain or DApp." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Distribute rewards to staked users" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Provide service discounts" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Distribute funds to community members for Network maintenance and development" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "What’s Next for Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth mode, we’re looking forward to our public marketing debut this month. Later in Q3 2022, we will announce the initial cohort of seven Founding Members, as well as a funding round. As our technology, governance, and network are novel, with no current analogies, we're excited to share more articles about key aspects of the Laconic Stack and Laconic Network, including our use of IPLD, the role of Watchers, the features of the Laconic App, and our pioneering governance framework." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For those wishing to run validators, we're aiming to have an incentivized Testnet in 2023, followed by the launch of our Mainnet later in 2023." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Summary of Laconic Network Milestones" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Public Marketing Launch: Q3 2022" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Coming out of a long stealth period, we’ll be launching our marketing assets and channels, including branding, website, social media and developer platforms, and content." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Seven Founding Members and Funding: Q3 2022" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ll be announcing our initial seven Founding Members and the close of our seed funding round." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentivized Testnet: Early 2023" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With the launch of our Incentivized Testnet, prospective validators will be incentivized to learn about the requirements for being a Member/Validator on Laconic Network and how to operate the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Mainnet Launch & Incentivized Global Data Cache: Late 2023" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Once Laconic Mainnet launches, " + }, + { + "type": "span", + "value": "and 7 Member/validators have joined the network, we’ll have an incentivized global data cache, allowing anyone to easily participate in serving blockchain data to DApps (and earn $LNT in doing so)." + } + ] + } + ] + } + } + }, + "date": "2022-07-26", + "featured": false, + "id": "24195830", + "image": { + "url": "https://www.datocms-assets.com/66113/1659030103-introducing-laconic.png" + }, + "slug": "introducing-laconic-network", + "title": "Introducing Laconic Network" + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/_blankResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/_blankResult.json new file mode 100644 index 0000000..e69de29 diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/aboutPageAllTeamsQuery.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/aboutPageAllTeamsQuery.json new file mode 100644 index 0000000..86aeda3 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/aboutPageAllTeamsQuery.json @@ -0,0 +1,186 @@ +{ + "data": { + "allTeams": [ + { + "memberGithub": "https://github.com/telefontelaviv-industries", + "memberLinkedin": "https://www.linkedin.com/in/joshua-eustis-29aa02245/", + "memberName": "Joshua Eustis", + "memberPosition": "Product & Ecosystem Lead", + "memberTeam": [], + "memberTwitter": "https://twitter.com/telefontelaviv", + "id": "31581314", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1657650667-je-headshot.jpg" + } + }, + { + "memberGithub": "", + "memberLinkedin": "", + "memberName": "7 Founding Members", + "memberPosition": "Announcing in Q3/2022", + "memberTeam": [ + "Laconic" + ], + "memberTwitter": "", + "id": "41347341", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1658133770-symbol_25.png" + } + }, + { + "memberGithub": "https://github.com/bmann", + "memberLinkedin": "https://www.linkedin.com/in/boris/", + "memberName": "Boris Mann", + "memberPosition": "CEO, Fission", + "memberTeam": [], + "memberTwitter": "https://twitter.com/bmann", + "id": "14097753", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1652367416-boris-mann.jpg" + } + }, + { + "memberGithub": "https://github.com/expede", + "memberLinkedin": "https://www.linkedin.com/in/brooklynzelenka/", + "memberName": "Brooke Zelenka", + "memberPosition": "CTO, Fission", + "memberTeam": [], + "memberTwitter": "https://twitter.com/expede", + "id": "14097761", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1652367483-brooklyn-zelenka.jpg" + } + }, + { + "memberGithub": "https://github.com/", + "memberLinkedin": "", + "memberName": "Roderik van der Graaf", + "memberPosition": "Founder, Lemniscap", + "memberTeam": [], + "memberTwitter": "", + "id": "17704626", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1653390516-captura-de-pantalla-2022-05-24-130822.png" + } + }, + { + "memberGithub": "", + "memberLinkedin": "https://google.com/", + "memberName": "Isabelle Kitze", + "memberPosition": "Co-founder, Atomic Form", + "memberTeam": [], + "memberTwitter": "", + "id": "17704633", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1653390421-captura-de-pantalla-2022-05-24-130542.png" + } + }, + { + "memberGithub": "", + "memberLinkedin": "", + "memberName": "Garrett Furo", + "memberPosition": "Co-Founder, Atomic Form", + "memberTeam": [], + "memberTwitter": "https://google.com/", + "id": "17704632", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1653390368-captura-de-pantalla-2022-05-24-130503.png" + } + }, + { + "memberGithub": "", + "memberLinkedin": "", + "memberName": "TBC", + "memberPosition": "Director, Incredulous / Advanced Blockchain", + "memberTeam": [], + "memberTwitter": "", + "id": "17704781", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1653391616-captura-de-pantalla-2022-05-24-132638.png" + } + }, + { + "memberGithub": "https://github.com/AFDudley", + "memberLinkedin": "https://www.linkedin.com/in/afdudley/", + "memberName": "Rick Dudley", + "memberPosition": "President", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "https://twitter.com/AFDudley0", + "id": "14097746", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1652367302-rick-dudley.jpg" + } + }, + { + "memberGithub": "", + "memberLinkedin": "https://google.com", + "memberName": "TBC", + "memberPosition": "Founder, Stake.Fish", + "memberTeam": [], + "memberTwitter": "", + "id": "17704779", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1653390762-captura-de-pantalla-2022-05-24-131232.png" + } + }, + { + "memberGithub": "https://github.com/i-norden", + "memberLinkedin": "https://www.linkedin.com/in/ian-s-norden/", + "memberName": "Ian Norden", + "memberPosition": "VP of Engineering", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "", + "id": "14097768", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1652367589-ian-norden.jpg" + } + }, + { + "memberGithub": "https://github.com/ashwinphatak", + "memberLinkedin": "https://www.linkedin.com/in/ashwinphatak/", + "memberName": "Ashwin Phatak", + "memberPosition": "Chief Architect", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "https://twitter.com/ashwinphatak", + "id": "14097767", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1652367533-ashwin-phatak.jpg" + } + }, + { + "memberGithub": "", + "memberLinkedin": "https://www.linkedin.com/in/leah-light-a308535/", + "memberName": "Leah Light", + "memberPosition": "Operations Manager", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "", + "id": "14097770", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1652367625-leah-light.jpg" + } + }, + { + "memberGithub": "https://github.com/erikdies", + "memberLinkedin": "", + "memberName": "Erik Dies", + "memberPosition": "Product Lead", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "https://twitter.com/thelesserdies", + "id": "14097771", + "memberImage": { + "url": "https://www.datocms-assets.com/66113/1652367650-erik-dies.jpg" + } + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllAuthorsQueryResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllAuthorsQueryResult.json new file mode 100644 index 0000000..deafcee --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllAuthorsQueryResult.json @@ -0,0 +1,42 @@ +{ + "data": { + "allAuthors": [ + { + "id": "63992470", + "name": "Zach Ramsay" + }, + { + "id": "63259964", + "name": "Maly Ly" + }, + { + "id": "55641250", + "name": "Joshua Eustis" + }, + { + "id": "55512259", + "name": "Stefan Adolf" + }, + { + "id": "55457832", + "name": "Michael Gushansky" + }, + { + "id": "44855867", + "name": "Robert Douglass" + }, + { + "id": "24856058", + "name": "Maly Ly" + }, + { + "id": "7023106", + "name": "Christoph Berger" + }, + { + "id": "2965423", + "name": "Mr. Lorem Ipsum" + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllCategoriesQueryResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllCategoriesQueryResult.json new file mode 100644 index 0000000..9ff2256 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllCategoriesQueryResult.json @@ -0,0 +1,31 @@ +{ + "data": { + "allCategories": [ + { + "id": "6311820", + "slug": "developers", + "title": "Developers" + }, + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "2965426", + "slug": "news", + "title": "News" + }, + { + "id": "3545001", + "slug": "partners", + "title": "Partners" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllPostsQueryResult - Copy.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllPostsQueryResult - Copy.json new file mode 100644 index 0000000..f7b6518 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllPostsQueryResult - Copy.json @@ -0,0 +1,12520 @@ +{ + "data": { + "allBlogPosts": [ + { + "slug": "rick-dudley-on-the-interop", + "title": "Rick Dudley Discusses Laconic Network on The Interop", + "date": "2023-02-09", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "/images/site_content/blogPost/rick-dudley-on-the-interop.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic cofounder Rick Dudley appeared on a special livestream of The Interop with host Sebastien Couture to discuss the Laconic Stack, the blockchain data problems that Laconic solves, Laconic’s novel governance structure, and how Laconic can index and verify data faster, more efficiently, and at lower cost. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Below is a distilled transcript of Rick’s responses during the discussion." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Future is App Chains" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think there will be millions of chains, and we'll be using a combination of rollups and mesh–not straight linear L1, L2, L3, but also meshes of rollups and attestations publishing bridges, etc. And although we may have millions of chains, we won't have millions of massive chains. A large chain may have 100 members, and there may be one or two chains out there with 4,000 validators. But in the world, you only need a few of those." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think everything becomes an app chain. I think mainnet Ethereum ultimately becomes an app chain and the application is settling rollups–very similar to Cosmos Hub, frankly. Polkadot, Ethereum 2.0, Cosmos Hub are all actually very similar in terms of the endgame state in the final thesis. And I don't think that there will necessarily be a winner per se. I think they will have curious different properties. " + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Why Laconic?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The ultimate goal of Laconic is to get all of the data that a user is concerned about in the hands of that user. Not in a cloud-hosted environment, not in Microsoft, not in AWS, but in users’ actual custody. And to enable them to do all the verification themselves." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Right now, it’s very difficult to extract parts of data from the Ethereum Mainnet that are relevant for Dapp needs. It’s almost impossible to synchronize a Geth node in a reasonable amount of time." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are multiple light client protocols that have come around to help alleviate this problem but they still don't go all the way. The Laconic Network goes the whole way. It goes from source code, to what is in the user's eyeballs with everything being verifiable. If you see a message on Laconic that came to you through the Laconic Network, you could say, \"I want to know which blockchain or blockchains this came from. I want to know what code generated this result. I want to know who wrote that code.” We provide all of that in the Laconic Network." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Three Major Components of Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is the Laconic LLC itself, which is in the Cayman Islands. There is the Laconic Stack, which is the standalone software that anyone can run today to generate this data and the evidence that they need. And then there's the Laconic Network, which facilitates the buying and selling of data. It facilitates running these services, discovering the services, paying for services, and then making sure all of that is verifiable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Those three components are an evolution. We've iterated on the Stack many times over at this point. MakerDAO is still using an early version of that stack to this day last I checked, which was recently. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you were an intrepid developer, you could go into the Stack Orchestrator code and run that yourself and put that into production yourself right now. But the problem with that is it's very expensive to generate this evidence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s computationally very expensive, and specifically, disk I/O operations are very expensive activities to do. So as a Dapp developer, when you have very few users, you can run this reasonably on the laptop. But as your app grows, or if you're wanting to see all of the Uniswap V3 pool data, then a laptop's not going to be able to process that in a timely manner necessarily. I mean, laptops are pretty powerful so some of them can, but maybe not all of them. And at that point, you need hardware. And when you need hardware, you then have this problem of, \"Okay, well am I going to buy hardware and rack it in a data center?\" That's probably not a viable answer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Am I going to go to AWS? Well, AWS is centralized, there are all sorts of problems. There's censorship for instance. AWS may choose to comply with a law that I'm not legally obligated to comply with. We've seen this issue with Alchemy and Infura, and these solutions comply with the laws in their jurisdiction, but the Dapp developer is in a different jurisdiction. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So then you end up with this situation; \"Okay, well if I want to have multiple service providers actually serving this data to users, they need to be in multiple jurisdictions.\" And that's what Laconic LLC solves. It's a Cayman Island LLC. We have members in different jurisdictions and those members will contract with the end users and comply with those laws in that way." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and Cosmos" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic team members were also core contributors to Cosmos SDK–we did a bunch of work on the Cosmos SDK. The data structures in Ethereum and the data structures in Cosmos and many other blockchains were designed to facilitate consensus, not to facilitate reading the data back out. And so in those architectures, there's utility in taking the techniques that we've applied to Ethereum and applying them to those other chains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is a value and utility to taking those techniques and applying them to the Cosmos SDK chains. Osmosis is an example of where it would be useful. For example, you can't have a block explorer that works across Cosmos Hub upgrades. No one's ever bothered to build one that works that way. If you built the block explorer on top of Laconic instead of directly on top of the chain, you would actually be able to provide that continuity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Every time a Cosmos chain upgrades, they regenesis and restart the chain. When you start that new genesis, people–just as a matter of convenience–don't preserve that data. You don't have a way of representing the irregular state change that happens during the upgrade. Whereas in the Laconic system, we have a means of doing all those things. We can link any two arbitrary chains together and we have a means of representing these arbitrary state changes. We can provide that continuity as a service." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentive Alignment " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because we're IPLD based, we actually can relatively easily take our archive and push it into Filecoin, where there can then be this clear monetization strategy for storing the data. Because we monetize the transmission of the data, which is a much easier problem to solve than the verifiable storage of Filecoin, we're providing an incentive for why someone would do that. Think about it. There are different incentives throughout the process. There's an incentive for including the transaction. That incentive is very clear, but there's not really any incentive in any blockchain I'm aware of for why I should then send that data. Why should I satisfy a read from a user? A user asks for a read, and why do I care?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That's what Laconic is trying to solve–we're incentivizing the reading of that data. And by incentivizing the reading of that data, that's step number two. Now we can talk about the incentives of step number three, which is a long-term persistent storage of that data. Because if you think about just having the incentives of just Filecoin and just Ethereum, you have this gap in the middle. Why do I take the Ethereum data and transform that and publish it to Filecoin? There's not really an incentive for me to do that." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whereas with Laconic, there starts to become more of an incentive to do that because I need to support my own read infrastructure. People will come to you and know to come to a single place to get their historic reads as well as their more recent reads. And so you’ll be incentivized to charge them. There will already be an ecosystem in place where people are accustomed to paying for data. And when they want to pay for old data or new data, they'll come to the same place, buy that data, and that will incentivize archival storage. Right now we don't have a very good model for why archival storage persists. And it is a real mechanism design issue actually." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "InterPlanetary Linked Data (IPLD) is the core of our system. The first thing we do is take the Ethereum data, which could be any blockchain or any hash linked data structure, and we convert that into an IPLD object. We then index it in that context. We’re storing the RLP encoded bytes, but we are also storing the CID (Content ID), the multi-format address of that object. That's how we're able to generate evidence." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On Ethereum, you have transaction receipts and you have the event messages. When you have an event message on Ethereum, the event message does not prove all the way back up to the root. So when you have a set of events, which is what The Graph consumes, the way that you prove that event is correct is that you find the block that that event was in, and then you rerun that whole block and at the end of it you see if you have the same event that you started with. Whereas, if I have an account balance on Ethereum, I have a block number and then I can get a proof. So I don't have to recompute the whole block to figure out the account balance in that block. I just get the proof from the Ethereum client about that account balance at that block, and I can present that proof and the balance to the user using eth_getProof." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But the actual logs in Ethereum are not provable in this way. This is why The Graph isn't provable and there are a lot of consequences from this. But because we use IPLD, we can create those hash links. Where the link was missing in the original Ethereum protocol, we can augment that protocol and generate a proof using the Ethereum data and our additional links, which are relatively easily. It's not some weird, crazy different format. It's this format that is very similar to the existing Ethereum formats, that prove that this log actually came from this block." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Member Validators" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic L2 has seven Founding Members right now. These seven Members validate, ingest the blocks, and make commitments to the state of those blocks. They then share that information with a paying customer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are plans to increase the validator set. From a customer perspective, if our customer is a Dapp developer and they're saying, “right now I have to use Infura, Alchemy, Blocknative to assert that my data is correct because if one of them goes down for whatever reason, that's three right there.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That sounds like a pain in the ass. With Laconic, you integrate one protocol and you get seven Member Validators instead of three, and you get an assertion from us that you can verify yourself that we're actually physically located in different places. Alchemy and Infura both run in AWS, I presume. If AWS goes down, you just lost two out of your three, if not all three out of your three in that case. Seven is a low number, but seven is incredibly high compared to what people have right now, or they think they have four and they have one, whereas we're positively asserting that you have seven." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "RPC Services and Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the path to building Watchers, we realized we had to build extremely performant RPC endpoints, and we had to build out a deployment system. We realized that that was actually what people wanted to buy from us. Most Dapps don’t want to bother with Watchers right now. What they want to see is this immediate savings on the RPC endpoint side. From there, oncet our foot is in the door, we can say, \"Well, we can give you even more savings. You are using that RPC endpoint to build your own indexer. We have a whole library of tools to build indexers that will auto-generate indexers for you. And we have a marketplace where you can go to get other people to run that indexer for you when you don't want to scale it.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Currently, RPC endpoints are subsidized by VCs. Dapp developers are never experiencing the true cost of running an indexing service or running an RPC endpoint. They're not exposed to that in a free market way. There's this actor, this venture capitalist, who is going in and giving away free samples at a massive scale. The challenge for us is in how we compete with that? There's also a challenge in that our customers are depending on this centralization service and don't realize it. " + } + ] + } + ] + } + } + }, + "featured": false, + "id": "64080923", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Rick Dudley Discusses Laconic Network on The Interop", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Rick Dudley Discusses Laconic Network on The Interop" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Rick Dudley Discusses Laconic Network on The Interop" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1675901476-laconic-seb-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1675901476-laconic-seb-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2023-02-09T00:27:23Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "erc20-watcher-demo", + "title": "ERC20 Watcher Demo", + "date": "2023-01-31", + "category": [ + { + "id": "6311820", + "slug": "developers", + "title": "Developers" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "image": { + "url": "/images/site_content/blogPost/erc20-watcher-demo.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In " + }, + { + "url": "https://www.laconic.com/blog/intro-to-the-laconic-stack", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the last blog post" + } + ] + }, + { + "type": "span", + "value": ", we introduced all the main components of the Laconic Stack. The first point of entry for any developer wishing to use Laconic is " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "span", + "value": ". It allows you to stand up a local fixturenet for testing purposes. Integrated directly into Stack Orchestrator are several \"stacks\" which work out of the box. Today, we'll be going over the ERC20 \"stack\" to provide an overview of some key components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You will accomplish the following:\n" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "stand up the core stack using Stack Orchestrator" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 token" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 Watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "send tokens to and from your local account to a new account on Metamask" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "use GraphQL to query the watcher for information about the token and accounts" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This tutorial assumes you are on a local machine (Mac or Linux). Trying it in the cloud requires additional configurations (e.g., opening ports) not covered here." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Pre-requisites" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "python3" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://www.python.org/downloads/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/get-docker/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker-compose" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/compose/install/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MetaMask " + }, + { + "url": "https://metamask.io/download/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "span", + "value": " in the supported browser of your choice." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If using a fresh Ubuntu Digital Ocean droplet, check out " + }, + { + "url": "https://github.com/LaconicNetwork/Laconic-Documentation/blob/staging/scripts/install-laconic-stack.sh", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this script" + } + ] + }, + { + "type": "span", + "value": " for a quick setup." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WARNING" + }, + { + "type": "span", + "value": ": if installing docker-compose via package manager (as opposed to Docker Desktop), you must install the plugin, e.g., on Linux:" + } + ] + }, + { + "code": "mkdir -p ~/.docker/cli-plugins\ncurl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose\nchmod +x ~/.docker/cli-plugins/docker-compose", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, install the latest release of Stack Orchestrator" + } + ] + }, + { + "code": "curl -L -o laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Give it permission:" + } + ] + }, + { + "code": "chmod +x laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Verify operation:" + } + ] + }, + { + "code": "./laconic-so \nUsage: python -m laconic-so [OPTIONS] COMMAND [ARGS]...\n\n Laconic Stack Orchestrator\n\nOptions:\n --stack TEXT specify a stack to build/deploy\n --quiet\n --verbose\n --dry-run\n --local-stack\n --debug\n --continue-on-error\n -h, --help Show this message and exit.\n\nCommands:\n build-containers build the set of containers required for a complete...\n build-npms build the set of npm packages required for a...\n deploy-system deploy a stack\n setup-repositories git clone the set of repositories required to build...\n version print tool version", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For a more permanent setup, move the binary to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "~/bin" + }, + { + "type": "span", + "value": "` and add it your `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PATH" + }, + { + "type": "span", + "value": "`." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so" + }, + { + "type": "span", + "value": "` CLI tool makes it easy to experiment with various components of the stack. It allows you to quickly and seamlessly experiment with watchers. Because it uses docker/docker-compose, several commands in this tutorial will leverage the ability to execute commands directly in the containers. This, for example, means that `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "yarn" + }, + { + "type": "span", + "value": "` doesn't need to be installed on your local machine." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Setup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Use the stack orchestrator to pull the core repositories:" + } + ] + }, + { + "code": "./laconic-so --stack erc20 setup-repositories", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see something like:" + } + ] + }, + { + "code": "Dev Root is: /root/cerc\nChecking: /root/cerc/go-ethereum: Needs to be fetched\n100%|####################################################################################################| 71.6k/71.6k [00:23<00:00, 3.10kB/s]\nChecking: /root/cerc/ipld-eth-db: Needs to be fetched\n100%|##########################################################################################################| 595/595 [00:00<00:00, 991B/s]\nChecking: /root/cerc/ipld-eth-server: Needs to be fetched\n100%|####################################################################################################| 25.5k/25.5k [00:06<00:00, 3.82kB/s]\nChecking: /root/cerc/watcher-ts: Needs to be fetched\n100%|####################################################################################################| 8.79k/8.79k [00:01<00:00, 4.49kB/s]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we'll build the docker images for each repo we just fetched." + } + ] + }, + { + "code": "./laconic-so --stack erc20 build-containers ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This process will take 10-15 minutes, go make a pot of coffee. The output will give you an idea of what's going on. Eventually, you'll see:" + } + ] + }, + { + "code": "Successfully built 77c75d57ad66\nSuccessfully tagged cerc/watcher-erc20:local", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, let's deploy this stack:" + } + ] + }, + { + "code": "./laconic-so --stack erc20deploy-system up", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The output will looks like this (ignore the warnings):" + } + ] + }, + { + "code": "WARN[0000] The \"eth_proxy_on_error\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_forward_eth_calls\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_http_path\" variable is not set. Defaulting to a blank string. \n[+] Running 23/23\n ⠿ ipld-eth-db Pulled 18.4s\n ⠿ 213ec9aee27d Already exists 0.0s\n ⠿ 85c3ef7cf9a6 Pull complete 0.7s\n ⠿ ac29cc04759a Pull complete 0.9s\n ⠿ 2a37e244d86b Pull complete 13.5s\n ⠿ 36d7202aa1cf Pull complete 13.8s\n ⠿ 3acdddb9790a Pull complete 13.9s\n ⠿ 9a938759f2bf Pull complete 14.1s\n ⠿ 5d65a6241248 Pull complete 14.2s\n ⠿ cee6999f074e Pull complete 14.4s\n ⠿ 20b12472cb73 Pull complete 14.8s\n ⠿ 65467bb36f5f Pull complete 16.2s\n ⠿ fe6050bae51d Pull complete 17.4s\n ⠿ 519306d43b4a Pull complete 17.9s\n ⠿ erc20-watcher-db Pulled 15.0s\n ⠿ 8921db27df28 Already exists 0.0s\n ⠿ eb286326f602 Pull complete 0.3s\n ⠿ 63139c77dd7e Pull complete 0.5s\n ⠿ 17baeacd3984 Pull complete 13.5s\n ⠿ 5f08b9782916 Pull complete 13.8s\n ⠿ a836be7ad658 Pull complete 14.0s\n ⠿ 1966853affaf Pull complete 14.2s\n ⠿ 4dc6d2c8dede Pull complete 14.4s\n[+] Running 8/8\n ⠿ Network laconic-30c27a9be20b005274dfc23fd7e90256_default Created 0.1s\n ⠿ Volume \"laconic-30c27a9be20b005274dfc23fd7e90256_erc20_watcher_db_data\" Created 0.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1 Healthy 33.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1 Healthy 34.8s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1 Started 32.7s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1 Started 33.1s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1 Healthy 53.5s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1 Started 54.3s", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's take stock of what just happened, we:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "cloned a bunch of repos: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so setup-repositories" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "built all of their docker images: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so build-containers" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed these images as services that know about each other: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so deploy-system up" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Take a look at all the running docker containers:" + } + ] + }, + { + "code": "docker ps", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You should see 6 containers:" + } + ] + }, + { + "code": "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n605ccf0e4461 cerc/watcher-erc20:local \"docker-entrypoint.s…\" 6 minutes ago Up 5 minutes (unhealthy) 0.0.0.0:3002->3001/tcp, 0.0.0.0:9002->9001/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1\n0a00a3a1bcd6 cerc/ipld-eth-db:local \"/app/startup_script…\" 6 minutes ago Up 5 minutes laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1\nf4aece866e48 cerc/ipld-eth-server:local \"/app/entrypoint.sh\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8081-8082->8081-8082/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1\nebe0dc8cd2b4 cerc/go-ethereum-foundry:local \"./start-private-net…\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8545-8546->8545-8546/tcp laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1\n72263d100b8c postgres:14-alpine \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 0.0.0.0:15433->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1\nd2effc54624c timescale/timescaledb:2.8.1-pg14 \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 127.0.0.1:8077->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, via the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "watcher-erc20" + }, + { + "type": "span", + "value": "` container, the " + }, + { + "url": "https://graphql.org/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "span", + "value": " playground is enabled on " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and you should check that it is there:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great so now we have the core stack up and running, let's deploy an ERC20 token." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, we need the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER ID" + }, + { + "type": "span", + "value": "` of the ERC20 watcher:" + } + ] + }, + { + "code": "docker ps | grep \"watcher-erc20\"", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Using the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ID" + }, + { + "type": "span", + "value": "` from the example above, we'll export the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER_ID" + }, + { + "type": "span", + "value": "` for use throughout the rest of the tutorial:" + } + ] + }, + { + "code": "export CONTAINER_ID=605ccf0e4461", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we can deploy an ERC20 token (currency symbol GLD):" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:deploy:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and your output should look like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-deploy\nDownloading compiler 0.8.0\nCompiled 5 Solidity files successfully\nGLD Token deployed to: 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\nDeployed at block: 9087 0x4dc63b4b2695b644d7d390d70c9de0232399ea4d54b8c75911eee14c13f9ceaf\nDone in 157.39s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, now that we've deployed the GLD token, you'll want to export its address for later use:" + } + ] + }, + { + "code": "export TOKEN_ADDRESS=0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Get your primary account address with:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn account:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and the following output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker account\n0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nDone in 21.63s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "export that address to your shell:" + } + ] + }, + { + "code": "export PRIMARY_ADDRESS=0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To get the latest block hash at any time, run:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "for an output like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker block-latest\nBlock Number: 12783\nBlock Hash: 0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\nDone in 21.44s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll configure MetaMask." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "MetaMask" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Open MetaMask in your browser:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Add Network\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Scroll to the bottow and click \"Add Network Manually\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Put in this information:" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you see the error above \"This URL is currently used by the Localhost 8545 Network\", change `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "localhost" + }, + { + "type": "span", + "value": "` to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "127.0.0.1" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We will come back to MetaMask later and complete this process; for now, copy your new address" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and export it for later:" + } + ] + }, + { + "code": "export RECIPIENT_ADDRESS=0x988a070c97D33a9Dfcc134df5628b77e8B5214ad", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Head on over to " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and paste the following (but with your variables):" + } + ] + }, + { + "code": "query {\n name(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n symbol(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n totalSupply(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "then click \"Run\" and you'll see a response like this:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"name\": {\n \"value\": \"Gold\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzavwb52aq6smf6movgcimvuoggp3cifayb2vyidg3ar546pwtb3dea\\\",\\\"ipldBlock\\\":\\\"0xf843a032575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85ba1a0476f6c6400000000000000000000000000000000000000000000000000000008\\\"}}}]\"\n }\n },\n \"symbol\": {\n \"value\": \"GLD\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzanp5bxcn6wqd2yptbbwo5o4rx3mhpji43yd7sfd42suq6hjuhuroq\\\",\\\"ipldBlock\\\":\\\"0xf843a03a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19ba1a0474c440000000000000000000000000000000000000000000000000000000006\\\"}}}]\"\n }\n },\n \"totalSupply\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzasfla7wzuessejihdtrxqd5lqxc57egukbbricizz2ssrltex4uvq\\\",\\\"ipldBlock\\\":\\\"0xeca0305787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace8a893635c9adc5dea00000\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here's what it'll look like in the browser:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A lot has happened thus far, so let's review; we've:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "downloaded the core repos, built their docker images, and launched a local network (all using stack orchestrator)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed an ERC20 token, added it to our MetaMask account" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "used the GraphQL playground to query the ERC20 watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "exported a handful of shell variables which are about to come in handy" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll use the playground to query account balances:" + } + ] + }, + { + "code": "query {\n fromBalanceOf: balanceOf(\n # latest block hash\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # primary account having all the balance initially\n # created by stack orchestrator\n owner: \"0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\"\n ) {\n value\n proof {\n data\n }\n }\n toBalanceOf: balanceOf(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # address copied from MetaMask, has no balance initially\n owner: \"0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "the primary address should have " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " 1000000000000000000000 and the recipient address should have 0:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzarsopkngjoijjyktfhgckq7te4dsk25gfyj653uxu4kcwqoyuykiq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5dea00000\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"0\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"\\\",\\\"ipldBlock\\\":\\\"0x\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note also that the recipient address also does not yet have a `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "cid" + }, + { + "type": "span", + "value": "` or `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ipldBlock" + }, + { + "type": "span", + "value": "`, which makes sense; this is a random account we just created and hasn't received any transactions. The network does not know about it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send it some GLD!" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:transfer:docker --token $TOKEN_ADDRESS --to $RECIPIENT_ADDRESS --amount 100000000 ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see a familiar output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-transfer --token 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550 --to 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad --amount 100000000\nNothing to compile\nTransfer Event at block: 13371 0x412dbc25599773bfe929c67882e4a001b9d1b3b8e1c60ad4a495d5306608c77a\nfrom: 0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nto: 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\nvalue: 100000000\nDone in 26.12s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now get the latest block hash:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and go back to the GraphQL playground. If you've changed nothing since the last query, update only the latest block hash and run the query again, you'll see the updated account balances:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"999999999999900000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzahg5shtf2rr7pompx7yx6r22zu4lea7ftlnbqkqcabvndsrvoljhq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5d8aa1f00\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"100000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzas6xotntgq4u3v4eui6pmtbyttgikzmu7mppknam2wrekhoynupjq\\\",\\\"ipldBlock\\\":\\\"0xe7a03305adb1a8efab310b21e03d5a9f08d8cf98815372c2c4d8068e1359b8f996bc858405f5e100\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, you've now used a watcher to see query token balances." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send some tokens from the MetaMask recipient account back to the primary account." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Recall that when adding the network to MetaMask, we used the currency symbol \"GLD\". However, this does not mean that MetaMask can auto-detect the token, therefore, we will have to manually import it:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Copy the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TOKEN_ADDRESS" + }, + { + "type": "span", + "value": "` and paste it in the popup. The two other fields should auto-complete:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Import Token\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and now you'll see your balance. Ignore the GLD token from earlier." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, send some tokens back to the primary address using MetaMask:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Make the gas price `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "0" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Grab the latest block hash (again) and fire off the GraphQL query for account balances to see the change." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Voila! You've successfully stood up the core Laconic stack, deployed an ERC20 token, and queried account balances." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cleanup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tear down your docker containers with:" + } + ] + }, + { + "code": "./laconic-so deploy-system --stack erc20 down", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Next steps" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Try out the " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator/tree/main/app/data/stacks/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "ERC721 demo" + } + ] + } + ] + } + ] + } + } + }, + "featured": false, + "id": "64023526", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "ERC20 Watcher Demo", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "ERC20 Watcher Demo" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "ERC20 Watcher Demo" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1675106863-laconic_clippy_watcher_02.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1675106863-laconic_clippy_watcher_02.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2023-01-31T18:52:55Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "intro-to-the-laconic-stack", + "title": "Intro to the Laconic Stack", + "date": "2023-01-18", + "category": [ + { + "id": "6311820", + "slug": "developers", + "title": "Developers" + } + ], + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "image": { + "url": "/images/site_content/blogPost/intro-to-the-laconic-stack.png" + }, + "content": { + "blocks": [ + { + "id": "63992474", + "title": "Laconic Stack Diagram" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here’s the main problem: reading data from the Ethereum blockchains is either cheap and sloppy or expensive and correct. As a result, Dapp developers have come to rely on inexpensive centralized services that do not provide evidence to verify the correctness of the data they are serving to Dapps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only is it expensive to get verifiable data but it can also be challenging to parse out the subset of data you really need. In the early days of SQL, you had to be proficient at the command line in order to use the product, and so use was limited to those that had that specialized capability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Eventually, GUIs were built such that anyone with basic computer skills could use drop-down menus and create database schemas without writing a single line of code. Web3 is still in the early days of SQL, it is not easy onboarding new users and developers who are otherwise quite capable with the latest Web2 technologies.\n\nRight now, there’s all this data on Ethereum and as a Dapp developer, you only want a tiny fraction of it. But, to verify that fraction, you have to (among several other things) maintain an archive node - this is prohibitively expensive for the majority of developers. To solve this problem, centralized services such as (Infura, The Graph, and Alchemy) have popped up and currently account for the majority (if not most) of Dapp queries to Ethereum." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic was created to address these and several other problems in the blockchain ecosystem. Not only does Laconic make it easy to get verifiable data - quickly and cheaply - it also provides a framework for data transformation and aggregation that are difficult or impossible to do in other systems.\n\nArchitecting a solution to this requires many moving pieces; these have been developed by core Ethereum & Cosmos contributors over the past 5 years. In this post, we will walk you through the various components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are three different ways to participate in the Laconic Network: Member Validators, Service Providers, and Dapp Developers. To describe the responsibilities and benefits of each role, we must first start grounded in the technicalities of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let’s take a look at the following core stack diagram:" + } + ] + }, + { + "item": "63992474", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note: this diagram intentionally leaves out several repositories (e.g., codecs, utilities, rpc shims). This is done for simplicity reasons and anyone diving deep into the stack will discover them.\n\nThe two repositories at the top are also the main entry points for most developers. `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` is a command-line tool for, well, orchestrating the stack. It uses docker-compose to deploy a specified collection of networked docker containers, thereby eliminating the need to set up a variety of services independently. Every user of the Laconic Stack will at some point - if not regularly - use the stack orchestrator." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watchers-ts" + } + ] + }, + { + "type": "span", + "value": "` repo contains the publically available Watchers and the code to generate them. Watchers are TypeScript that is generated from one or more Solidity smart contracts. Dapp Developers can participate in the Laconic Network by either 1) writing a custom watcher for their Dapp or 2) writing a generally useful watcher and publishing it to the Laconic Registry, thus earning a fee every time it is used. We’ll come back to Watchers later." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Down at the bottom left is the `" + }, + { + "url": "https://github.com/cerc-io/laconicd", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconicd" + } + ] + }, + { + "type": "span", + "value": "` repository and it is indeed the “bottom” of the stack. It is built from the Cosmos SDK and has custom modules specific to operating the Laconic Network (e.g., fork of Ethermint/Evmos, auction, nameservice). It is likely that in the future there will be a public testnet, however, because the Laconic Network is a permissioned validator set, only Member Validators that have officially joined the Laconic Network will be included in the mainnet. Just because the validator set is permissioned does not prohibit anyone from running a full node and Service Providers or others may choose to do so for a variety of reasons." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/laconic-sdk", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-sdk" + } + ] + }, + { + "type": "span", + "value": "` is a library for facilitating talking to `laconicd`. Both the `" + }, + { + "url": "https://github.com/cerc-io/laconic-registry-cli", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-registry-cli" + } + ] + }, + { + "type": "span", + "value": "` and the `" + }, + { + "url": "https://github.com/cerc-io/laconic-console", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-console" + } + ] + }, + { + "type": "span", + "value": "` use it. While `laconic-registry-cli` is a command-line tool for doing so, the `laconic-console` is a user interface for writing and reading records on the Laconic Network. These general-purpose tools are useful for a wide variety of use cases across the stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A key goal of the Laconic Network is to provide accurate, verifiable data from the Ethereum blockchain. Comparisons to currently available solutions are for another post, however, no service currently exists to provide inexpensive evidence that the data being served is correct. The Laconic solution (one of) to this is in something called “statediffing”, a part of the stack run by Member Validators and, likely by Service Providers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It starts with a maintained fork of `" + }, + { + "url": "https://github.com/cerc-io/go-ethereum", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "go-ethereum (geth)" + } + ] + }, + { + "type": "span", + "value": "` that has an added real-time state-diffing service. Statediffing gives a clear picture of the state between any given blockheights. This allows Laconic to minimize the amount of computation required for providing proofs. Three additional “helper” services perform different tasks required to get a full picture of the state as required by an application. Together, these comprise the Full Index Node (FIN)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-service" + } + ] + }, + { + "type": "span", + "value": "` provides historical state data, while the `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-fill-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-fill-service" + } + ] + }, + { + "type": "span", + "value": "` uses the historical state data to fill statediff gaps as required. Finally, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-state-snapshot", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-state-snapshot" + } + ] + }, + { + "type": "span", + "value": "` loads a complete state at a certain blockheight, which helps to bootstrap the system. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nThere is more to be written about statediffing, however, what’s important to note here is that each service is writing independently to `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-db", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-db" + } + ] + }, + { + "type": "span", + "value": "`. The latter serves as a bucket for state data that has been indexed in IPLD. Rather than querying this database directly, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-server", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-server" + } + ] + }, + { + "type": "span", + "value": "` provides an API layer for Watchers to easily query relevant pieces of data from the Ethereum state. Additionally, `ipld-eth-server` recapitulates the native Ethereum JSON RPC interfaces on top of the `ipld-eth-db` database." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "And so we’ve come full circle back to the Watchers. As previously mentioned, they are generated from one or more Solidity smart contracts and configured to query specific pieces of data relevant to a Dapp. Watchers make it easy to query the data you need from Ethereum " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "and" + }, + { + "type": "span", + "value": " - along the way - get evidence in order to generate proofs that your data is correct. This is in contrast to currently available solutions for Dapp developers, who must currently rely on centralized providers that don’t provide evidence to generate proofs." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Web3 is (still!) in its early days, and like the early days of SQL, Dapp developers need advanced knowledge of complex data structures to build their Dapp. Watchers simplify this by exposing a GraphQL endpoint, a solution familiar to an order of magnitude more developers than querying the Ethereum blockchain directly." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is building a suite of tools to address core problems in Web3. Today, we’ve provided an overview of the main components of the Laconic Stack. Developers interested in Laconic should start with `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` to get a sense of running different parts of the stack, then check out `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watcher-ts" + } + ] + }, + { + "type": "span", + "value": "` to experiment with different watchers and progress to making their own." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "63992475", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Intro to the Laconic Stack", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Intro to the Laconic Stack" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Intro to the Laconic Stack" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1673986992-laconic_clippy_grid2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1673986992-laconic_clippy_grid2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2023-01-17T20:35:35Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "trends", + "title": "A Look Ahead at Crypto in 2023", + "date": "2023-01-05", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "/images/site_content/blogPost/trends.png" + }, + "content": { + "blocks": [ + { + "id": "63885761", + "title": "Prop 82" + }, + { + "id": "63885758", + "title": "Akash Validator Set" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So much has changed in the last year due in large part to a seemingly unpredictable sequence of epic market failures. Despite this, we’re still here trying to predict the unpredictable. As the dust settles from a cascade of collapses including FTX, Terra, 3AC, and many others, the direction crypto must take to become viable is now clearer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We find ourselves in a strange position. We built protocols that actually work under immense market pressure, showing some of the promise of decentralized systems. However, that pressure came from deeply irresponsible market manipulation and behavior. How do we make sense of the fallout, and do better. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Blockchain Regulation: it’s coming" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, this space is about to get regulated. The " + }, + { + "url": "https://www.pillsburylaw.com/en/news-and-insights/digital-commodities-consumer-protection-act-digital-assets.html", + "type": "link", + "children": [ + { + "type": "span", + "value": "Digital Commodities Consumer Protection Act" + } + ] + }, + { + "type": "span", + "value": " (DCCPA) looms in the wake of FTX’s demise and Sam Bankman Fried’s increasingly radioactive support of the bill. The DCCPA aims to give the CFTC primary jurisdiction over crypto exchanges. This would establish the commission as the authority on spot, margined and leveraged digital commodity trades. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The bill, however, does not outline processes for classifying assets as commodities and securities. This is relevant because the Securities and Exchange Commission (SEC), which under Gary Gensler has increasingly strived for more oversight, currently qualifies most crypto, aside from Bitcoin, as a security. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In Europe the new Markets in Crypto Assets Regulation (MiCA) aims to regulate dollar-pegged stablecoins, controlling transaction count and volume limits which, " + }, + { + "url": "https://www.reuters.com/technology/eu-crypto-rules-set-cap-dollar-pegged-stablecoins-2022-10-07/#:~:text=LONDON%2C%20Oct%207%20(Reuters),competitiveness%2C%20industry%20representatives%20have%20said.", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "according to Reuters" + } + ] + }, + { + "type": "span", + "value": ", are already exceeded by the three largest stablecoins, Binance USD, USD Coin & Tether. Stablecoin volume has increased dramatically over the last several years and a cap or ban could hinder the crypto ecosystem overall. Crypto regulation and the conversations around asset classifications, regulatory purview, and stablecoin usage will be defining battlegrounds in 2023. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Decentralization: yes it matters" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Increasing regulatory pressure will force crypto projects to re-focus on achieving actual decentralization; not just to avoid legal consequences but also to empower more equitable and effective on-chain governance. Although there is no official guidance from the SEC, commissioner " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Hester Pierce’s Safe Harbor Proposa" + }, + { + "type": "span", + "value": "l has served as a guiding light for those interested. The proposal suggests networks must achieve sufficient decentralization within three years, meaning networks should not be controlled by a single individual or group of individuals. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tokens must also be used to exchange value pertaining to the network. The token should be sold for facilitating access to, participation on, or development of the network. Essentially, if it’s a decentralized network, validators/investors/teams cannot control majority stake. If the network has a token, then the token needs to be used for something " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "actually" + }, + { + "type": "span", + "value": " intrinsic to network operations–it can’t just be a fundraising vehicle." + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "On-Chain Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve touched on a number of external forces impacting networks but what about internal forces? On-chain governance is rapidly evolving. Usage of proof of stake (PoS) consensus has grown over the last several years in part due to a dramatic reduction in power consumption, and also because of increased flexibility to affect significant changes to network parameters. With voting power relative to stake, stake concentrated in the hands of a few entities can present unique and potentially insurmountable challenges to achieving sufficient decentralization." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Cosmos ecosystem is a particularly fascinating testing ground for on-chain governance. From the controversial " + }, + { + "url": "https://www.mintscan.io/juno/proposals/16", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Juno Prop 16" + } + ] + }, + { + "type": "span", + "value": " which resulted in on-chain confiscation of user funds, to the equally controversial " + }, + { + "url": "https://www.mintscan.io/cosmos/proposals/82", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Cosmos Prop 82" + } + ] + }, + { + "type": "span", + "value": " which put forward an entirely new paradigm for Cosmos Hub utility and ATOM value capture, the Cosmos ecosystem has been trailblazing governance processes and implementation. " + } + ] + }, + { + "item": "63885761", + "type": "block" + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What’s at Stake?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the main issues we’re seeing now is the disproportionate concentration of stake. In Cosmos for instance, the top 5 out of 150 " + }, + { + "url": "https://www.mintscan.io/juno/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Juno validators" + } + ] + }, + { + "type": "span", + "value": " control 25% of the network. The top 5 out of 175 " + }, + { + "url": "https://www.mintscan.io/cosmos/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Cosmos validators" + } + ] + }, + { + "type": "span", + "value": " control 25% as well. The top 5 out of 100 " + }, + { + "url": "https://www.mintscan.io/akash/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Akash validators" + } + ] + }, + { + "type": "span", + "value": " control 37%. " + } + ] + }, + { + "item": "63885758", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In Ethereum, " + }, + { + "url": "https://cointelegraph.com/news/64-of-staked-eth-controlled-by-five-entities-nansen", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "according to Nansen" + } + ] + }, + { + "type": "span", + "value": ", there are 5 entities that control 64% of staked Ether. Although only 11% of circulating ETH is staked, among 426,000 validators, three major cryptocurrency exchanges account for 30% of the total staked ETH, with Lido DAO accounting for 31%. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If the top 4-5 Validators control 1/4 of the stake and, at times more, of a network, is the network really decentralized? If an exchange like Binance or Huobi can buy up large volumes of tokens, enter the top validator slots, and through governance, make substantive changes to the network for their own benefit, is that network truly decentralized? There are valid arguments on both sides. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There’s an argument that these networks are permissionless and any entity can engage in governance by purchasing tokens and staking them, so in theory, they are decentralized. In practice, power is often concentrated in the hands of a few entities with a majority of the tokens. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The current Cosmos governance landscape is also fascinating because many pivotal governance mechanisms and network changes are being deliberated simultaneously. Should there be a constitution? Should there be larger deposit requirements for proposals? Should ATOM issuance be changed to fund network growth initiatives–completely repurposing the network in the process? Cosmos will continue to innovate on-chain governance. As Cosmos gains more visibility, developers will adapt components of Cosmos governance for their purposes. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Novel Solutions to Crypto’s Most Existential Problems" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As we look ahead to 2023, Laconic is well positioned to address these issues. Laconic Network leverages Cosmos technology and is run by an LLC that is designed to comply with the laws within its jurisdiction and to allow each member to comply with the laws within their jurisdiction. Geographic distribution of member validators strengthens the network's resilience to nation specific sanctions. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Additionally each member validator is given one vote and all votes are equally weighted. This eliminates uneven distribution of voting power typically seen in delegated proof of stake networks. Laconic Network also makes it clear that Laconic Network Token (LNT) is treated as a loyalty point and for prepayment of services, and is not a security or currency. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For more on the Laconic Network Governance model and how it addresses the fundamental issues around asset classification, token usage, PoS networks and regulation, you can check out “" + }, + { + "url": "https://www.laconic.com/blog/laconic-governance-model", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "A New Governance Model for the New Web" + } + ] + }, + { + "type": "span", + "value": ".” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Crypto isn’t going anywhere, but we will need to build safer and more decentralized systems, and work within a more aggressive regulatory environment. " + } + ] + } + ] + } + } + }, + "featured": false, + "id": "63853186", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "A Look Ahead at Crypto in 2023", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "A Look Ahead at Crypto in 2023" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "A Look Ahead at Crypto in 2023" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1672273151-screen-shot-2022-12-28-at-4-19-05-pm.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1672273151-screen-shot-2022-12-28-at-4-19-05-pm.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2023-01-04T22:13:31Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "laconic-governance-model", + "title": "A New Governance Model for the New Web", + "date": "2022-10-25", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "/images/site_content/blogPost/laconic-governance-model.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network was founded to develop crucial infrastructure for current and future generations of blockchains and Web3 applications. That's an ambitious mission, and one that demands significant technological innovation. Perhaps even more innovative, however, is the company's governance model—a unique organizational " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "structure that, along with the Laconic token, underlies" + }, + { + "type": "span", + "value": " a scalable and sustainable project with the power to address the many operational, regulatory, and ideological challenges of Web3. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Operational challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network’s governance model addresses four primary operational challenges:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Network funding. The Laconic governance model funds initial network development and growth. But unlike most venture capital-funded businesses, Laconic avoids passive investment, instead requiring all founding members to make significant contributions of capital, engineering expertise, or both." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Network growth. To ensure timely continued growth, the Laconic Network must scale along with demand for Web3 data services—necessitating clear procedures for adding and removing Members and Service Providers." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Hardware operations. Laconic indexing and services are computationally expensive, requiring significant hardware and infrastructure investment. Laconic is creating a decentralized infrastructure network built for scalability and flexibility, and designed to deliver data to consumers at the highest possible service level." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Corporate governance. Clear and fair governance is essential to any decentralized project. The Laconic limited liability company agreement (LLCA) clearly specifies how stakeholders including Members, Service Providers, and data customers may participate in the governance process; proposal submission and voting processes; and proposal requirements for specific changes and updates." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Regulatory challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The governance model addresses three primary regulatory challenges: " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Making it clear that the Laconic LNT token is solely a loyalty point or prepayment for services—not a security or currency" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ensuring that on-chain private auction of membership interests in a Cayman Island LLC is fully compliant with US securities law" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Protecting minority rights for all shareholders of Laconic LLC" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Ideological challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As Web3 scales, Laconic Network aims to become an essential layer in service of verified blockchain data to applications. The company's governance model must be designed to:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Reward active Network participants through incentives for actions that help build and run the network, including becoming Members or Service Providers, writing Watchers, and consuming network data" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Discourage attempts to speculate on, or otherwise passively profit from, the network" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Corporate structure" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic LLC structure lays out a flexible but binding legal framework for the company's relationship with Network Members. It also provides for treasury management and related off-chain governance solutions while the network is being built:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network, as a corporate entity, is a Cayman Islands LLC made up of members who are themselves corporations. Members work together to build and operate the Network, for which they are required to act as Service Providers and Validators; as a Validator, each member controls a share of the Laconic Network Liquidity Pool. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The corporation's purpose is to sell data in a way that provides purchasers with cryptographic evidence, which ensures reimbursement if a Service Provider fails to meet a quality of service guarantee. Each membership interest in Laconic LLC is indivisible and nontransferable, and represents an actual security per the S.E.C. definition of the term." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On-chain membership interests are determined based on a combination of each Member's capital accounts and individual Liquidity Pool shares—a structure designed to reward members for acting as Validators and Service Providers while promoting healthy competition. The Members’ treasury collateralizes the Network, protecting Service Providers and users." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network currently has seven Founding Members, who brought the project into existence by funding its treasury and providing the engineering work to create the Laconic Stack and Laconic App. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Founding Members have been selected to ensure enough initial Validators and Service Providers to run the hardware needed to index and serve data to end users." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Requiring that Members both fund the Network and provide technical services ensures that all Members are motivated to make the project successful. There is no passive investment—for example, from VCs who buy the token at a discounted price to fund the Network, then wait for a pump to sell. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Selecting seven Founding Members supports accountability for a minimum viable level of decentralization. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members operate within multiple legal jurisdictions, preventing the Laconic Stack, Validators, and Service Providers from concentrating in too few data centers or cloud providers." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As demand for the services provided by Laconic Network increases, it will be necessary to add Members in order to increase the capacity and diversity of available Service Providers. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As Members are added, Membership totals must always take the form of 6n+1—ensuring that simple majority votes will never be evenly split, and that Validators will never be paid for Byzantine fault tolerance (BFT) that they do not provide. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "New Members are added via auction. To participate in an auction, potential Members must prove that they are technically qualified and have the funds needed to buy a Membership Interest from one or more existing members." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To demonstrate their ability to operate the Laconic Stack at a level of quality comparable to that of existing Members, potential Members must participate in a testnet." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Potential Members who successfully complete the testnet are invited to a private auction of a lot of six new Membership Interests. Auction proceeds are added to the Liquidity Pool, minus a Seller’s Reward for existing Members. Seller’s Rewards, akin to mini liquidity events, provide additional financial motivation for membership. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "At some point in the future, Laconic LLC may choose to change the rules regarding membership and Validator participation." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As required by law, provisions exist by which Members may be voluntarily or involuntarily removed from the Network in order to maintain minority shareholder rights of the membership as a whole." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members wishing to exit may sell their interest back to Laconic LLC, which will then auction it to a new potential Member. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members failing to fulfill Validator or Service Provider duties may be evicted by a governance resolution. " + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Laconic Network Token" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Much of the mechanism design of the Laconic Network is encapsulated in the intended uses of the Laconic Network Token (LNT)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": " LNT provides insurance to the Network's Service Providers and data consumers. Any prepayment for services, or any surplus services that data consumers have paid for but not used, may be redeemed for an asset of value. For Service Providers, LNT simplifies business operations, serving as a single asset representing claims against both a stablecoin and the native currency of the L1 on which they are required to operate. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The LNT also provides guarantees to Network participants, in much the same way that initiating a retail transaction triggers an automatic temporary hold on the buyer's payment card, which is released when the purchase is completed. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note that  LNT’s purpose is to allow for refundable prepayment of services among a federation of Service Providers operating across multiple jurisdictions; the token does not exist in any other capacity. It is not a security, as it does not represent a financial interest in Laconic LLC; it can be redeemed only via authorized transfer within the Liquidity Pool, which is controlled by the Laconic Members. Membership Interests are, however, securities, as they represent both the financial interests and the governance stakes of the Members in Laconic LLC; one Membership represents one vote." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The LNT is considered a voucher for data retrieval, not a general-purpose currency. LNT is procured through the Liquidity Pool by trading 3CRV or ETH, and used to pay Service Providers for data transfers. LNT is also staked by Service Providers, and enables them to register on-chain as such. A reclamation function ensures that the full fixed supply of the token will remain constant and in use at all times, making it unsuitable for long-term holding or speculative investment. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Corporate governance for Laconic LLC will be performed on chain, by voting parties who are both Members and Validators. Voting parties may vote on two types of resolutions:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Ordinary resolutions" + }, + { + "type": "span", + "value": " govern operational details of the network and require a ⅔ vote to pass." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Special resolutions" + }, + { + "type": "span", + "value": " are required to amend the governance articles or shut down the Network. Special resolutions must be passed by a unanimous vote." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Proposals may be submitted by any user of the Network who has more than the equivalent of $1000 USD escrowed as LNT." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "New possibilities for organizational governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s primary goal is to make verifiable blockchain data available at scale to Web3 applications. The company also aims to embody the ideological goals of true decentralization and fair rewards for parties actively participating in the network as Members, Validators, Service Providers, or Watcher writers. The Founding Members have also thought extensively about the utility of the LNT token (as a voucher for data), its role in governance (none at all), and how to grow the network through Validator/Member auctions. Together, these choices yield a novel corporate governance model worthy of study by any organization with similar goals and challenges." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "55641249", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "A New Governance Model for the New Web", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "A New Governance Model for the New Web" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "A New Governance Model for the New Web" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1664245622-governance-blog-4.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1664245622-governance-blog-4.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-12-15T16:07:29Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "monthly-update-dec-2022", + "title": "Laconic Monthly Update: December 2022", + "date": "2022-12-01", + "category": [ + { + "id": "2965426", + "slug": "news", + "title": "News" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/monthly-update-dec-2022.png" + }, + "content": { + "blocks": [ + { + "id": "63259974", + "title": "metamask" + }, + { + "id": "63259975", + "title": "testnet" + }, + { + "id": "63259976", + "title": "lp" + }, + { + "id": "63259977", + "title": "public marketing launch" + }, + { + "id": "63260091", + "title": "nft" + }, + { + "id": "63259978", + "title": "events" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Hello Laconians," + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Welcome to our first Laconic Monthly Update! " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth development, we were excited to go live with our public marketing launch in the last quarter." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the three months since, Laconic has grown quickly, and we’ve made huge progress with both product and growth. Despite the arrival of “crypto winter,” our team remains focused on building a platform with the power to ensure that the next generation of blockchain applications are safer, faster, and more useful." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thank you for joining us on our journey!" + } + ] + }, + { + "item": "63259974", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "METAMASK PARTNERSHIP" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This month, we announced that Laconic is partnering with the world’s leading self-custodial wallet, ConsenSys’s MetaMask, and MetaMask founder Dan Finlay, to launch MobyMask, an anti-phishing tool. Laconic's MobyMask-caching Ethereum light client greatly reduces the cost of hosting a trustworthy copy of the MobyMask anti-phishing registry, making the tool accessible and affordable for a wide range of users." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Together, we’re laying the groundwork for an ambitious shared vision: a system with the ability to verify credible sources for a wide range of information types. You can " + }, + { + "url": "https://www.laconic.com/blog/laconic-and-consensys-metamask-launch-mobymask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "read our blog to learn more about the partnership with MetaMask" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259975", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TESTNET" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve successfully launched a testnet for internal development and for testing with Laconic’s Founding Members (Founding Members to be announced). The genesis transaction occurred on August 9, 2022, with all Members in consensus.\n\nWe’re now creating tooling to monitor block production, number of transactions, and Validator health; further tooling will include a dashboard and a block explorer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A public, incentivized testnet is planned for 2023. You can sign up for early access " + }, + { + "url": "https://www.laconic.com/partners#testnet", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "here" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259976", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "LIQUIDITY POOL & PAYMENT CHANNELS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve continued work on the mechanics and logistics of the liquidity pool, state/payment channels that will govern the flow of payments and tokens between our own chain and Ethereum, and auction logic implementation. We’ll also be building out the back end of the Laconic App and its network underpinnings. The next step is to deploy the smart contract for the liquidity pool directly to testnet. " + }, + { + "url": "https://www.laconic.com/blog/introducing-laconic-network", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Learn more about our platform and network" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259977", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PUBLIC MARKETING LAUNCH" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth, we completed the design and development of the Laconic website and community channels, with a successful public launch on August 16. Since then, we’ve scaled online engagement with potential partners, developers, press, and people around the world looking to learn more about Laconic. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "item": "63260091", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WEEKLY GROWTH CAMPAIGNS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve launched 14 campaigns since the website launch, and will continue to scale marketing and community efforts in 2023." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "July 26:" + }, + { + "url": "https://www.laconic.com/blog/introducing-laconic-network", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Introducing Laconic Network" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "July 26:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-different", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Network is Different" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "August 6:" + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watchers: Ensuring Trustlessness in Web3" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "August 30:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Network Uses IPLD" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 6:" + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watchers: Ensuring Trustlessness in Web3" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 13:" + }, + { + "url": "https://www.laconic.com/blog/rick-dudley-on-interchain-fm", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Rick Dudley Discusses Laconic Network on Interchain.fm" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 20:" + }, + { + "url": "https://www.laconic.com/blog/what-is-a-proof", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "What Is a Proof and Why Do You Need One?" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 28:" + }, + { + "url": "https://www.laconic.com/blog/99-problems-but-nfts-aint-one", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "99 Problems But NFTs Ain’t One" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 5:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-improves-the-nft-experience", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Radically Improves the NFT Experience" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 20: " + }, + { + "url": "https://www.laconic.com/blog/laconic-devcon-vi-recap", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic’s Devcon VI Recap" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 25: " + }, + { + "url": "https://www.laconic.com/blog/laconic-governance-model", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "A New Governance Model for the New Web" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "November 8: " + }, + { + "url": "https://www.laconic.com/blog/laconic-and-consensys-metamask-launch-mobymask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic and ConsenSys’s MetaMask Launch MobyMask" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "November 22: " + }, + { + "url": "https://www.laconic.com/blog/why-decentralization-matters", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Why Decentralization Matters" + } + ] + } + ] + } + ] + } + ] + }, + { + "item": "63259978", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "EVENTS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Nothing beats real-life engagement! Conferences and events are key channels for growing audience awareness. Our team canvassed select Ethereum and blockchain conferences with a focus on brand positioning, partnerships, and customer development:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ETHAmsterdam and Devconnect (4/18–25): " + }, + { + "type": "span", + "value": "Going into the launch of our public marketing channels, our team produced " + }, + { + "url": "https://twitter.com/laconicnetwork/status/1521957074841706497", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "a networking event" + } + ] + }, + { + "type": "span", + "value": " to connect with Member teams, investors, and developers attending ETHAmsterdam and Devconnect, a conference dedicated to Ethereum L2 Scaling." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ETH CC Paris (7/18–22): " + }, + { + "type": "span", + "value": "Multiple Laconic Member teams converged at the largest Ethereum conference. This was a great opportunity for us to deepen awareness of Laconic among investors and validators in the Ethereum community. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cosmoverse Medellin (9/27–28): " + }, + { + "type": "span", + "value": "Cosmos’s largest conference was an opportunity for us to build awareness and engagement in the Cosmos ecosystem." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Devcon Bogota (10/7–16): " + }, + { + "type": "span", + "value": "The largest Ethereum developer conference offered the perfect opportunity to drive awareness of Laconic with Ethereum developers. Check out the " + }, + { + "url": "https://www.laconic.com/blog/laconic-devcon-vi-recap", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "recap blog" + } + ] + }, + { + "type": "span", + "value": " to learn what the hot topics were at Devcon." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "SF Blockchain Week and ETH SF (11/1–5): " + }, + { + "type": "span", + "value": "These two Bay Area conferences enabled our team to deepen our network in one of the world’s premier tech hubs." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\n\n" + } + ] + } + ] + } + } + }, + "featured": true, + "id": "63259979", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Laconic Monthly Update: December 2022", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Laconic Monthly Update: December 2022" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Laconic Monthly Update: December 2022" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1669870811-laconicupdate-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1669870811-laconicupdate-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-12-01T17:51:49Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "why-decentralization-matters", + "title": "Why Decentralization Matters ", + "date": "2022-11-22", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "/images/site_content/blogPost/why-decentralization-matters.png" + }, + "content": { + "blocks": [ + { + "id": "61461493", + "title": "Blockchain Trilemma" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The emergence of any technology brings with it new language, and changes or expands the possible meanings of existing terms. Blockchain, of course, is no exception—and almost any discussion of it will include lots of talk about decentralization. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We should probably start with a definition. In the context of blockchain, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralization" + }, + { + "type": "span", + "value": " denotes the transfer of ownership and governance to the many from the few. Think of the differences between Prince, a solo songwriter and performer who insisted on absolute and final control over his artistic output, and the Beatles, a band that (despite their well-known internal power struggles) had no designated “lead” singer or sole songwriter. While decentralization may not require absolute accord, it does locate power across a system rather than in one designated point. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s worth noting that many in the Web3 world use the terms " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralized " + }, + { + "type": "span", + "value": "and " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distributed" + }, + { + "type": "span", + "value": " interchangeably" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": ", " + }, + { + "type": "span", + "value": "but this is technically incorrect. In the context of the blockchain, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distributed" + }, + { + "type": "span", + "value": " refers not to ownership or governance, but only to the distribution of parties (in most cases, servers) across a physical or digital space.\n\nIn this context, decentralization isn't a binary value. Instead, it describes the values and characteristics of a system that deliberately locates significant aspects of power across a defined system, rather than at a single point within it. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "But, like, what does decentralization " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "do?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is one of the primary innovations that powers blockchains. As the number of parties participating in the consensus mechanism of a blockchain increases, so does its level of decentralization. Participants' consensus about a given digital truth replaces the need for individual trust agreements, while establishing credible censorship resistance for anyone who wants to " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "do something" + }, + { + "type": "span", + "value": " on that blockchain. Any transaction is agreed upon by all, which allows any two parties to transact without first establishing a specific shared trust. This removes the need for oversight by a third party—for example, a \"financial panopticon\" established to prevent fraud—and reduces associated privacy and security risk. " + } + ] + }, + { + "item": "61461493", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization plays a key role in the Blockchain Trilemma—the need to balance decentralization, security, and scalability. A blockchain's level of decentralization is directly proportional to its ability to withstand attacks from inside or outside the network, and directly correlated with its level of neutrality, with greater decentralization linked to stronger censorship resistance and spam-resistant pricing. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most exciting things about decentralization is that the digital truth agreed upon via consensus may be proven by anyone running a node. Network participants must uphold the established consensus when making new blocks, or their stake will be slashed—they'll lose a significant portion of the benefits of entry. And while block producers are referred to as " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "validators" + }, + { + "type": "span", + "value": ", they perform no defined validation action. Instead, they have a financial incentive to act upon the truth determined by a decentralized quorum of participants." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As the number of participants in a blockchain increases, so does the power of decentralization, preventing participants from stealing someone else’s assets by manipulating transactions submitted for block inclusion. Each member controls the private key for a specific address, so no one can censor or manipulate a transaction once it’s been submitted. In the broadest sense, this system operates counter to centralized payment protocols such as PayPal, which censors transactions and even " + }, + { + "url": "https://www.forbes.com/sites/emilymason/2022/10/27/after-paypal-revokes-controversial-misinformation-policy-major-concerns-remain-over-2500-fine/?sh=2e5a914c30c4", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "levies fines on users for not obeying a set of internally derived rules." + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Until recently, says Laconic cofounder and chief protocol designer Rick Dudley, “we’ve mostly been concerned with usurious intermediaries. So maybe it’s best to think about " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "disintermediation" + }, + { + "type": "span", + "value": " as a primary goal within a decentralized system.\" Profit-driven intermediation, he notes, \"is also far more difficult to add to a well-decentralized system, which resists usury by nature.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Look, I don’t care about jpegs or digital Monopoly money. What’s in it for me?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization doesn't just increase the security of financial assets ... or ape GIFs No one party controls the decentralized network, so " + }, + { + "url": "https://www.coindesk.com/business/2022/06/29/coinbase-is-reportedly-selling-geo-location-data-to-ice/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "your pedigree information can't be given or sold to corporations, governments, or any other third parties without your consent" + } + ] + }, + { + "type": "span", + "value": ", as is dismayingly common on Web2 platforms. Decentralization can also protect network members from arbitrary moralities of a market subset—think the ejection of sex workers from Craigslist, protesters losing their financial platforms under political pressure, or citizens prevented from transacting by their government." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Anything else?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization also maximizes data reliability. A single point of failure can affect huge swaths of the public; imagine the effects of destroying the only server farm servicing a specific bank. Of course, to mitigate the risks associated with power outages, simple server failures, or even terrorist attacks, banks geographically " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distribute" + }, + { + "type": "span", + "value": " their servers, with multiple parties storing multiple copies of data in multiple places across a " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralized" + }, + { + "type": "span", + "value": " network. " + }, + { + "url": "https://en.wikipedia.org/wiki/Byzantine_fault", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Even if a large portion of its participants were to go dark, the network would still function." + }, + { + "type": "span", + "value": "\n\n" + } + ] + }, + { + "type": "span", + "value": "In this scenario, the bank is protected—though you, as a user of the bank, are vulnerable to having your transactions watched, censored, or thrown out altogether. The contents of your account can also be " + }, + { + "url": "https://ij.org/report/policing-for-profit-2/grading-state-federal-civil-forfeiture-laws/irs-cleans-out-bank-accounts/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "seized for potentially arbitrary or extralegal reasons" + } + ] + }, + { + "type": "span", + "value": ". As the recent " + }, + { + "url": "https://cointelegraph.com/news/bitcoin-sinks-to-new-yearly-low-at-16-8k-as-ftx-insolvency-fears-turn-into-contagion", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "FTX meltdown" + } + ] + }, + { + "type": "span", + "value": " made all too clear, centralized cryptocurrency exchanges are even more precarious—and because of their inherent lack of regulation, " + }, + { + "url": "https://www.forbes.com/sites/haileylennon/2022/08/01/bankrupt-crypto-lender-celsius-could-leave-customers-last-in-line-to-get-paid/?sh=4c4005ea5fde", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "prone to leave retail investors holding the bag" + } + ] + }, + { + "type": "span", + "value": ". A more decentralized system could offer individual users far more protection against institutional controls, not to mention far less potential for reckless mismanagement by those holding the keys." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Still a long way to go" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralizing a protocol or network still poses many challenges. Foremost among them is the fact that " + }, + { + "url": "https://www.weforum.org/agenda/2016/05/joseph-stiglitz-are-markets-efficient-or-do-they-tend-towards-monopoly-the-verdict-is-in/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "markets tend to direct capital to the few and not the many" + } + ] + }, + { + "type": "span", + "value": ". Our current way of life is undergirded by a system that counters decentralization at every level, moving power away from the majority. Accepting decentralized systems in the physical world would require changing much of how we think about how society is arranged, and about " + }, + { + "url": "https://www.latimes.com/opinion/op-ed/la-oe-pascrell-live-nation-concert-ticketing-20180517-story.html", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "rapidly shrinking" + } + ] + }, + { + "type": "span", + "value": " access to competition under late capitalism." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Another key point: Governments that cannot monitor private transactions often attempt to sanction users of protocols that protect the anonymity afforded by robust, decentralized censorship resistance. Such moves can be used to" + }, + { + "url": "https://home.treasury.gov/policy-issues/financial-sanctions/sanctions-programs-and-country-information/iran-sanctions", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "uphold international sanctions" + } + ] + }, + { + "type": "span", + "value": "—or " + }, + { + "url": "https://www.wsj.com/articles/crypto-advocacy-group-sues-u-s-treasury-over-tornado-cash-sanctions-11665610506?mod=article_inline", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "oppress a government’s own citizenry" + } + ] + }, + { + "type": "span", + "value": ". " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most glaring problems of decentralization is Web3 spaces' level of volatility. While money laundering has existed since the beginning of money (and would continue even if all the blockchains in the world were to vanish), there's enough money laundering, wash trading, and socially engineered theft on blockchain networks to lead regulators to sanction, " + }, + { + "url": "https://www.coindesk.com/policy/2022/08/21/arrest-of-tornado-cash-developer-draws-dutch-crypto-community-protest/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "or even imprison" + } + ] + }, + { + "type": "span", + "value": ", participants and even developers—while hinders true decentralization across the ecosystem. If we want to make the ecosystem safe for everyone, we've got a lot of housecleaning to do." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "So where does Laconic come in?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is a core feature of the Laconic Network, delivering all of the benefits we've described—uptime, security, affordability, censorship resistance, and fair, consensus-driven governance. The network's seven founding Members, and 60+ additional Members spread across multiple jurisdictions, each run their own hardware, making it highly resistant to concentrated server failures, tampering by individual bad actors, and hostile takeovers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Each Member serves the network by running " + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Watchers," + } + ] + }, + { + "type": "span", + "value": " smaller caches of specific data commonly used by, for example, DApp developers. There's no single point of failure—if any one Service Provider fails, other Members can ensure Watcher uptime." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization will drive even more benefits as the network matures. Laconic Network customers can purchase data directly from Service Providers, which compete to win customers through low prices, at the same time exerting downward pressure on data prices across the industry." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, while geographically diverse traditional networks can be subject to an incredibly complex web of national and local laws, participants in the decentralized Laconic Network need comply only with those laws governing their own jurisdictions. “In each jurisdiction,\" explains Dudley, \"varying and often contradictory regulations or sanctions can interfere with or contradict one another, making global compliance difficult. The goal is to be compliant in our jurisdiction, while allowing our users to be compliant in each of theirs.” Each Laconic Stack user decides how best to comply with the laws of their jurisdiction. The biggest benefit here, though, is that under this model, no single governing body has the power to determine a specific flow of data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is at the heart of Web3, and key to all major benefits of the blockchain. As a structure, it has the power to offer the most benefit to the largest group of people, making it more difficult for the few to hoard power or capital at the expense of the many. But for blockchain, and the larger Web3 ecosystem, to reach its full potential, we must work toward broad understanding of the benefits and pitfalls of decentralized networks. Only then can we begin to calm the waters of Web3, and remove the sharks for the next billion users we hope to bring with us." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "61461416", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Why Decentralization Matters ", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Why Decentralization Matters " + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Why Decentralization Matters " + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1669073122-central_decentral_009.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1669073122-central_decentral_009.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-11-21T23:35:25Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "how-laconic-different", + "title": "How is Laconic different?", + "date": "2022-07-26", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/how-laconic-different.png" + }, + "content": { + "blocks": [ + { + "id": "38579992", + "text": "Laconic is a new indexing and querying solution that aims to make verifiable blockchain data available from a truly decentralized and massively scalable network. We are not the first project to address this problem, and this article will describe how our approach sets us apart from the work that has been done before." + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "item": "38579992", + "type": "block" + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Vision" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We believe that Web3 and blockchain technologies offer the promise of " + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "permissionless and equal access" + }, + { + "type": "span", + "value": " to new digital tools and financial instruments, and that these properties can be a force for positive change. Critically, decentralization of the networks and resources in Web3 is the key protection against the monopolistic jurisdiction of large tech companies and overzealous governments.  " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We further believe that the creation of public, consensus driven, cryptographically verifiable data leads to greater transparency and accountability. Finally, we believe that transacting on blockchains fosters incentive alignments through tokenomic engineering, allowing us to be more deterministic about how systems and society run." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These are the core values that imbue all of the design decisions in creating the Laconic Network. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The challenges facing Web3 adoption are significant, and threaten to prevent the fulfillment of the promise and vision. Laconic Network is focusing on a set of challenges that arise when DApp developers need to interact with blockchains and query the data that is stored there. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Working with blockchain data is more challenging than traditional web development because of the storage constraints, the overhead of preserving consensus and protecting networks from attack. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Other significant challenges include: " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Blockchain nodes are designed to be write-optimized and bear large overhead costs for storage, network gossip, and computation required for consensus and security." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "True internet scale growth (eg. comparable to Facebook or Twitter) is not possible on existing Web3 backend infrastructure. Mass adoption will require the development of a data scalability layer that facilitates for Web3 the types of usage patterns that we expect from Instagram, Google, and LinkedIn." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These challenges all compound when cross chain data is called for, which is increasingly the case. The data scalability layer will also need to account for cross-chain interoperability." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic isn’t the first to address the challenge of building a data availability layer for Web3. Companies such as Infura and Alchemy have long offered blockchain data services as convenient APIs for DApp developers, and have thus facilitated the early growth of high utility applications that interact with the underlying chains. As centralized services with quickly growing traction and clout, they already in some ways resemble the monopolistic centralized players of Web2. The convenience they offer is paid for at the price of losing censorship resistance and permissionless access. In short, we trust these companies to tell us the truth and treat us right. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Somewhat truer to the ethos of blockchain and Web3 are The Graph and Covalent, both of which have recently launched products that have elements of decentralization and verification of the data being served. For Ethereum, these services are limited to indexing events, transactions, and internal transactions, and don’t typically index the state and storage trees at all. One consequence of this is that they don’t support the Ethereum JSON RPC get_proof() call, which is essential to safeguarding the verifiability of the data being queried. No proof, no trustworthy data. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Why will Laconic’s solution solve current challenges?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network is innovating on three separate fronts: technology, governance, and incentive alignment. The challenge of serving verifiable blockchain data, at scale, from a decentralized platform is so enormous that a purely technical solution won’t suffice. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Technology" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "At a technology level, we have pioneered multiple innovations. " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD structures to preserve the hash-linked data format that is native to blockchain data—even withstanding transformations." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The concept of the Laconic Full Index Node which removes the need to re-index every time a new data access use case is materialized. The result is significantly faster setup times for customized data access API endpoints, that are tailored to individual DApps." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Re-indexing of blockchain data into specialized caches (Watchers) to simplify anticipated DApp use cases. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Content-addressability of blockchain data. In the same way that IPFS is used to request files by content hashes, blockchain data will be requested by hashes of its content, and served through the off-chain p2p IPFS network. " + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "This leads to a global hyperscale caching layer that is sufficient enough to ensure the viability of Facebook or Google level adoption of Web3." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Working with multiple international legal teams, Laconic Network is pioneering a novel governance model with the aims of ensuring unprecedented decentralization, fairness, and equality. The goals are simple: maintain a decentralized network that resists being monopolized and which is not subject to the whims of any single entity’s jurisprudence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In order to achieve those goals, a number of problems must be addressed:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A sufficient number of Validators and Service Providers to scale as Web3 grows" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A rich diversity of underlying network and data center dependencies (e.g. we aim to avoid ending up with a preponderance of machines running on AWS)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A diversity of jurisdictional locations for network Members" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A diversity of funding sources for network members" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All governance decisions occur on-chain" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All members have equal voting power" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "No early token allocations for VCs or founders" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Incentive Alignment" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the prime benefits of blockchains is the ability to prescribe token incentives that align with desirable behaviors. For example, validators are incentivized to do the work needed to secure the network and establish consensus about block generation. The Laconic Network has created a number of incentives to align the many roles involved towards an ever more efficient and growing data availability layer for Web3." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For data consumers, it’s important to have a true selection of Service Providers (node operators who run Watchers). Since Service Providers must compete with each other on the basis of price and service level, this benefits consumers by guaranteeing the best level of service for the lowest price." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Developers writing Watchers (to be run by Service Providers), are incentivized to create useful Watchers. There is a mechanism for rewarding developers when people choose to run and query the Watchers they have written. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Members have a strong incentive to grow the network to meet demand through the fulfillment of new member auctions. These auctions bring new liquidity to the network as well as new members who can increase network capacity by indexing and serving data. A bonding curve rewards existing members with an auction fee with the earliest members benefiting the most. However, there is a disincentive to growing the network too quickly, as earnings for Members will come from indexing and serving data. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Anyone interested in building out the network will have a token incentive and opportunity to participate simply by running an in-browser cache of the off-chain data. By caching the data in this way, it will form an infinitely scalable global hyper-cache of the data, and the service level of DApps will increase as a result. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Conclusion" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic aims to significantly contribute to the growth and adoption of Web3 and blockchain technologies by solving foundational problems that currently hamper the development of DApps. This involves indexing blockchain data and making it available from a decentralized network of service providers, and doing so in a way that retains the cryptographic verifiability of that data. The innovations that allow us to do this emanate not only from technological advances, but also from the novel governance and incentivisation structures that are built into the very network structure. " + } + ] + } + ] + } + } + }, + "featured": true, + "id": "35743439", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "How is Laconic different?", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "How is Laconic different?" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "How is Laconic different?" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1657616658-laconic_differentiators_7.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1657616658-laconic_differentiators_7.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-11-09T17:36:58Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "laconic-and-consensys-metamask-launch-mobymask-light-client", + "title": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client", + "date": "2022-11-08", + "category": [ + { + "id": "3545001", + "slug": "partners", + "title": "Partners" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/laconic-and-consensys-metamask-launch-mobymask-light-client.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "blockquote", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "\"It’s hard to overstate what an achievement this [Laconic] is for bringing down the computational costs of privacy, scalable data distribution, and also speed of data lookups for users who repeatedly use the same contracts but don’t necessarily run their own full nodes.\"" + } + ] + } + ], + "attribution": "Dan Finlay, MetaMask Founder" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Today I’m thrilled to announce that " + }, + { + "url": "http://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-clienthttps://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic is partnering with the world’s leading self-custodial wallet, ConsenSys’s MetaMask" + } + ] + }, + { + "type": "span", + "value": ", and MetaMask founder Dan Finlay, to launch " + }, + { + "url": "https://mobymask.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "MobyMask" + } + ] + }, + { + "type": "span", + "value": ", an anti-phishing tool. You can check out Dan Finlay’s " + }, + { + "url": "https://mirror.xyz/0x55e2780588aa5000F464f700D2676fD0a22Ee160/8whNch3m5KMzeo6g5eblcXMMplPf8UpW228cSh3nmzg", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "original MobyMask blog" + } + ] + }, + { + "type": "span", + "value": ", and his " + }, + { + "url": "https://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-client", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "blog announcement today" + } + ] + }, + { + "type": "span", + "value": ", for more information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MobyMask is a community-sourced registry for managing and reporting phishing accounts across social media and Web3 intersections, based on MetaMask's " + }, + { + "url": "https://mirror.xyz/0x55e2780588aa5000F464f700D2676fD0a22Ee160/pTIrlopsSUvWAbnq1qJDNKU1pGNLP8VEn1H8DSVcvXM", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Delegatable framework" + } + ] + }, + { + "type": "span", + "value": ". It provides robust tools for sourcing phishing reporters from across online communities, using a dynamic web of trust. The registry is designed to grow organically as community members report scammers and phishers directly onto the Ethereum mainnet." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Accessible, affordable privacy and security" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic's MobyMask-caching Ethereum light client greatly reduces the cost of hosting a trustworthy copy of the MobyMask anti-phishing registry, making the tool accessible and affordable for a wide range of users. By optimizing the blockchain query process, explains Dan Finlay in his blog announcement today, Laconic \"greatly reduces the computational costs of privacy, scalable data distribution, and also speed of data lookups for users who repeatedly use the same contracts but don’t necessarily run their own full nodes.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MobyMask further streamlines blockchain security by bringing light-client functionality directly to the browser. The key is the " + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watcher" + } + ] + }, + { + "type": "span", + "value": ", an essential piece of the Laconic Stack that caches only the specific blockchain data required for a particular query—reducing data requirements by orders of magnitude while creating a lightweight, self-hostable process that web services such as MetaMask, WalletGuard, and Phishfort can use to draw MobyMask phishing detection data. A soon-to-be-released TypeScript version of our Watcher will streamline security even further, with support for fully browser-based list caching and peer-to-peer data replication. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Lowering barriers for entry to Web3 " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Phishing and related scams plague both novice and experienced blockchain users, creating further barriers to adoption and limiting the potential for growth of the Web3 ecosystem. That’s created a real need for tools that make this ecosystem safer, faster, easier, and more affordable to use. In reducing technical requirements for interacting with the blockchain, the Laconic protocol lowers barriers to entry, making Web 3 technologies more accessible to those with limited resources." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With that goal in mind, Laconic is working on an update that lets users search a dynamic repository of phishing reports, and share them with a network of peers, via a free private API. Messages will be fully provable on chain, with blockchain used only to resolve registry conflicts and revoke access when needed. While the initial repository is by invitation only, Laconic and MobyMask plan to eventually allow users to subscribe to multiple roots of trust, and to host their own. Meanwhile, we’re laying the groundwork for an ambitious shared vision: a system with the ability to verify credible sources for a wide range of information types." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "A highly scalable and privacy-preserving application" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With MobyMask making Web3 access safer and less stressful for everyone involved, the potential benefits, says Dan, are substantial. ”If we’re going to build anything of value out of decentralized technology, we need to basically eliminate phishing. That's going to take a lot of creativity and ingenuity, and we’re happy that " + }, + { + "url": "https://www.laconic.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic" + } + ] + }, + { + "type": "span", + "value": " and MobyMask combine so well to deliver a highly scalable and privacy-preserving application whose safety remains rooted on the blockchain.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nYou can learn more about MobyMask at " + }, + { + "url": "http://www.mobymask.com", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "mobymask.com" + } + ] + }, + { + "type": "span", + "value": ". For more information on Laconic, " + }, + { + "url": "https://discord.com/invite/ukhbBemyxY", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "join the Laconic Discord" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "56055548", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1667863936-partnership-template-updated.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1667863936-partnership-template-updated.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-11-08T20:40:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "laconic-devcon-vi-recap", + "title": "Laconic’s Devcon VI Recap", + "date": "2022-10-20", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "/images/site_content/blogPost/laconic-devcon-vi-recap.png" + }, + "content": { + "blocks": [ + { + "id": "55844240", + "title": "MEV" + }, + { + "id": "55844241", + "title": "OFAC" + }, + { + "id": "55844702", + "title": "Regulation" + }, + { + "id": "55846103", + "title": "Devcon" + }, + { + "id": "55844242", + "label": "Join Our Discord to Learn More", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As the first Devcon held in 3 years, and right after the historic Merge, Devcon VI drew thousands of attendees from around the world and our team was fortunate to be a part of it.\n" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So what were the hot topics at this year’s Devcon VI in Bogota?" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "\n" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "1. Maximum Extractable Value (MEV)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most pervasive topics at Devcon was MEV. Flashbots co-founder Phil Daian announced the launch of SUAVE, Single Unifying Auction for Value Expression, to combat MEV centralization and censorship.\n\nSUAVE will be an MEV-aware encrypted mempool that maximizes profits for users. " + } + ] + }, + { + "item": "55844240", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "2. Office of Foreign Assets Control (OFAC)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Post Merge, OFAC compliant MEV boost relays have been enabled across a broader array of block proposers resulting in a censorship of 51% of Ethereum blocks. The implication is that if block producers can censor tx’s due to OFAC compliance, what other types of transactions could they censor?" + } + ] + }, + { + "item": "55844241", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "3. Regulation: Digital Commodity Consumer Protection Act (DCCPA) " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Regulation isn’t keeping up with the speed of innovation. This is obvious for instance in Europe’s current attempt to to regulate dollar-pegged stablecoins, capping volumes and potentially banning algo-stablecoin use. Stablecoin volume has increased dramatically over the last several years and a cap or ban could significantly hurt the crypto ecosystem overall.\n\nThe Digital Commodity Consumer Protection Act (DCCPA) could have a significant impact on DeFi in the US as well. The general consensus is that the crypto community as a whole has to advocate for positive outcomes, and resource groups like Coin Center that are actively fighting for regulatory guidance. " + } + ] + }, + { + "item": "55844702", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "5. Layer 2" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unsurprisingly, the increasingly crowded and competitive L2 landscape was on display.–The Arbitrum and Optimism booths  angled for attention side by side at the conference, an example of the escalating war for dominance between the major Layer 2 solutions. There was discussion around the security tradeoffs between side chains like Polygon and rollups like Arbitrum & Optimism, along with concerns that rollups are currently too centralized and lacking fraud proofs. " + }, + { + "url": "https://www.laconic.com/blog/what-is-a-proof", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Laconic knows a thing or two about proofs" + } + ] + }, + { + "type": "span", + "value": "…\n" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "6. Data Scaling" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Data indexing and scaling continues to be a fundamental challenge for Web3 development. You’re either running a full node yourself or forced to make compromises while using one of a handful of existing centralized indexing solutions.\nEnter Laconic, the first multichain verifiable data indexer. Laconic enables developers to build internet-scale Web3 applications and light clients at a fraction of the speed and the cost of current tools." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "*Side note, the conference wifi pw was “runfafullnode” but we’re here to do that for you…IYKYK.\n" + } + ] + }, + { + "item": "55846103", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Interestingly, the decision to host the conference in South America was prescient as the economic crises in Venezuela and Argentina highlight the use cases of crypto as a hedge against inflation and more generally as a means for financial sovereignty. We spoke with a number of Argentinians and Venezuelans about the growing usage of crypto payments in their countries.\n\nThe need to enable developers to build applications with access to faster and more reliable data has never been greater. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "item": "55844242", + "type": "block" + } + ] + } + } + }, + "featured": false, + "id": "55844243", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Laconic’s Devcon VI Recap", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Laconic’s Devcon VI Recap" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Laconic’s Devcon VI Recap" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1666289454-devcon-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1666289454-devcon-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-10-20T18:53:41Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "how-laconic-improves-the-nft-experience", + "title": "How Laconic Radically Improves the NFT Experience", + "date": "2022-10-05", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "/images/site_content/blogPost/how-laconic-improves-the-nft-experience.png" + }, + "content": { + "blocks": [ + { + "id": "55708950", + "label": "Discord", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Part 1 of this two-part series discussed the five major implementation and integration issues plaguing DApp developers, along with an overview of how Laconic addresses each one:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "1. NFT data lacks consistency and integrity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "2. NFT data is fragmented and scattered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "3. For both NFTs and equivalent token formats, data types vary from blockchain to blockchain." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "4. NFT games require lightning-fast retrieval and scalability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "5. Data retrieved from multiple locations is difficult to verify. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now, in Part 2, we'll explore how these problems are playing out in the real world—and how Laconic tools and processes shield both users and developers from the effects of shifting and conflicting standards, radically reducing everyone's list of problems.   " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Apes, witches ... tax fraud? The power of caching." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's start by looking at a current cornerstone of the industry. Arguably the most popular use case for NFT artwork is " + }, + { + "url": "https://boredapeyachtclub.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "10k PFP collections" + } + ] + }, + { + "type": "span", + "value": ", collections of (typically 10,000) relatively simple anthropomorphized cartoon jpegs, generated by combining variations on a large but limited set of defined metadata “traits,” each of which correlates to a physical attribute of the generated character drawing. " + }, + { + "url": "https://boredapeyachtclub.com/#/home", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Bored Ape Yacht Club" + } + ] + }, + { + "type": "span", + "value": ", for example, uses this model. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the case of another popular collection, " + }, + { + "url": "https://www.cryptocoven.xyz/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Crypto Coven" + } + ] + }, + { + "type": "span", + "value": ", a smart contract is called to mint tokens based on a similar list of randomly generated traits. To gather each piece of this data and display it, a wallet client or platform makes an API call to an " + }, + { + "url": "https://docs.metamask.io/guide/rpc-api.html#table-of-contents", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "RPC" + } + ] + }, + { + "type": "span", + "value": " provider. To retrieve the data, the client or platform must sift through an extraordinary amount of data to find just a few essential pieces. And all that searching eventually adds up. More serious problems could also arise, from sandwich MEVs to lowball ape sales as a " + }, + { + "url": "https://www.cryptonews.net/news/nft/6583225/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "front for tax shenanigans" + } + ] + }, + { + "type": "span", + "value": ".\n\nLuckily, there's an easier (and less easily abused) option. A Laconic Watcher could cache all data related to a given smart contract—in this case, " + }, + { + "url": "https://cryptocoven.mirror.xyz/A622VSRm8-9oLzc8l3oFGmfnFUZQmDQ3Wx3ObhSlhsc", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the brilliant Crypto Coven `counters.sol` variant" + } + ] + }, + { + "type": "span", + "value": "—making it instantly available at any DApp request while preserving proof of authenticity. Because Laconic can prove metadata at the current block height, not several blocks behind like most current centralized data providers, all NFT data received is guaranteed correct. That makes for faster queries, lower costs, easier updates—a quality-of-life upgrade for developers. For a particularly busy smart contract such as Bored Ape Yacht Club, Laconic's data cache could also drastically improve the daily experience of thousands of users. And let's not forget the damage done to industry reputation by " + }, + { + "url": "https://nftevening.com/bored-ape-6462-sold-for-200/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the successful exploits of every \"fat-fingered\" Ape owner" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Where we’re going, we don’t need standards." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Smart contracts are incredibly popular for good reason. But there are " + }, + { + "url": "https://etherscan.io/address/0x53e4c0167ed855e96f562dbb911854d586f5cc07#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "countless" + } + ] + }, + { + "type": "span", + "value": " " + }, + { + "url": "https://etherscan.io/address/0x517e643f53eb3622fd2c3a12c6bfde5e7bc8d5ca#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "variations" + } + ] + }, + { + "type": "span", + "value": " out there, even on Ethereum. In fact, " + }, + { + "url": "https://www.smartcontractresearch.org/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "entire Web2 and Web3 developer communities" + } + ] + }, + { + "type": "span", + "value": " view this aspect of NFT technology as where its real beauty is revealed: the developer as artist, Making It New. Limit contract and metadata standards, the argument goes, and you'll hamstring developers' creativity, starving the ecosystem of innovative ideas. (Unaddressed standards are, of course, nothing new. Think back to the browser wars of Web 1.0, when entire websites ... just didn't work on Internet Explorer, and at many design shops, ensuring IE functionality would cost you a good 20% extra.)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is uniquely suited to solve the problems associated with nonstandardized token data retrieval. By caching token data into microchains of IPLD blocks, and serving it directly to DApps from the resulting decentralized content-addressable database in a unified format, the Laconic Watcher makes the entire issue moot. It displays the precise data queried, confirms that it's correct—and does so more quickly and with a more current state than any standard RPC service." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "An end run around fragmented and scattered metadata." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A Laconic Watcher makes it easy for developers to retrieve blockchain data from an indexed database; fetch off-chain data as needed; and correlate, merkleize, and cache the results. With a " + }, + { + "url": "https://etherscan.io/address/0xe6ddda1c3f1cb01aa5c86a21e8636deabfd1f013#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Crypto Coven smart contract" + } + ] + }, + { + "type": "span", + "value": ", for example, there's no need to concern yourself with both the smart contract and the media the tokenURI points to—a Watcher can tie them together in ways standard RPC providers can't, with a terrific signal-to-noise ratio, perfectly up-to-date state, and persistent uptime.\n\nWatchers define and expose precisely crafted APIs for DApps that consume specific data sets, such as a given smart contract. Allowing a DApp itself to focus solely on its primary mission of delivering the goods to the user eliminates the need for (often surprisingly heavy) query lifts." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Smooth token data retrieval and management, even from multiple blockchains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Non-fungible tokens exist on multiple blockchains, in varying degrees of complexity, in a range of programming languages, in line with wildly varying (if any) metadata standards. The result is, unsurprisingly, significant friction when it comes to token management. Laconic removes much of the drag by unifying and extending the possibilities of metadata once it’s been retrieved." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To maintain a unified format regardless of chain or data source, Laconic stores all blockchain data in content-addressable data formats. This allows for a level of global availability and extensibility that makes DApp development far more convenient. " + }, + { + "url": "https://www.laconic.com/blog/99-problems-but-nfts-aint-one", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Our hypothetical on-chain Library of Alexandria in Part 1" + } + ] + }, + { + "type": "span", + "value": " is made possible by Laconic’s uniquely decentralized, content-addressable format and the Watcher's computationally light footprint. Think of this chain-agnostic view as a Rosetta Stone for Web3 data." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic makes querying NFT data verifiable, fast, and affordable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic makes DApp access both faster and easier with its up-to-date, indexed, verifiable blockchain data and custom query and caching services; queries, too, become more affordable to develop and maintain. And in a time of ballooning NFT data stores and rapidly increasing demands on user experience, Laconic Watchers deliver data at a fraction of the cost of traditional data retrieval." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In short, the architecture of the Laconic Network delivers practical solutions to many of today's most pressing NFT challenges. Custom Laconic Watcher services can, for example:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collect data from multiple blockchains" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Process that data to make it consumable by DApps" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Preserve data verifiability across transformations " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Keep data up to date" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Streamline DApp data retrieval" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With these capabilities, the Laconic Network becomes an indispensable data querying and verification layer for any NFT-related service—and, more than any other approach to querying blockchain data, a resource with the capacity to help the entire industry thrive." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Read more about Watchers and the Laconic Network " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "here" + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Got questions? Join us on Discord." + } + ] + }, + { + "item": "55708950", + "type": "block" + } + ] + } + } + }, + "featured": true, + "id": "55708951", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "How Laconic Radically Improves the NFT Experience", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "How Laconic Radically Improves the NFT Experience" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "How Laconic Radically Improves the NFT Experience" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1664922912-laconic_nfts_003-2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1664922912-laconic_nfts_003-2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-10-05T15:24:49Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "99-problems-but-nfts-aint-one", + "title": "99 Problems But NFTs Ain’t One", + "date": "2022-09-28", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "/images/site_content/blogPost/99-problems-but-nfts-aint-one.png" + }, + "content": { + "blocks": [ + { + "id": "55644653", + "title": "Tweet" + }, + { + "id": "55644654", + "label": "Discord", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ask most people what they know about NFTs, and it’s likely you’ll hear a lot about digital art, tokenized authenticity, proof of ownership, and/or a hot new asset class (or, depending who you hang out with, commodity fetishism and the death knell of late-stage capitalism). But hot takes aside, today’s market for generative artwork is only the tip of the iceberg. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whatever the use case—and the possibilities are vast—today’s NFT ecosystem is generating mountains of largely unstructured data, both on and off chain. And without established technical standards to ensure clear structure and verifiability, it’s all too easy for that data to become the stuff of developer nightmares." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "More possibilities, more problems." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As use cases for NFTs continue to expand beyond " + }, + { + "url": "https://superrare.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "1/1 works of art" + } + ] + }, + { + "type": "span", + "value": " and " + }, + { + "url": "https://boredapeyachtclub.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "10k pfp collections" + } + ] + }, + { + "type": "span", + "value": ", developers building applications that integrate on-chain tokens face a widening maze of challenges around data management and queries. . The power of an NFT lies in its ability to represent any unique entity, with today’s common use cases including in-game tokens, editioned generative artwork, event ticketing, and even " + }, + { + "url": "https://www.theblock.co/post/134923/artist-blows-up-lamborghini-to-make-nfts-in-protest-against-crypto-culture", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "physical works or goods" + } + ] + }, + { + "type": "span", + "value": ". Blockchain and non-fungible tokens, in this case, make use of smart contracts, which can be written in a variety of programming languages, using varying methods of metadata storage and retrieval. The digital asset to which a given piece of metadata refers, and the metadata itself, can each be located almost anywhere in the decentralized web—which is itself both immeasurably large and continuously expanding. \n\nCombine a diversity of programming languages and an absence of standardization, and you end up with some novel problems, mostly involving how to handle the sheer volume of data generated, how to locate it in the many places it might be stored (both on chain and off), and how to handle inconsistent data formats and structures. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network was created to address these challenges through shared standards that make it possible for DApp developers to quickly and intuitively integrate and manage disparate NFT data and assets. In this piece, the first of a two-part series, we look at five major NFT implementation and integration issues—along with what we’re doing to solve each one, so you can sleep at night. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "1. NFT data lacks consistency and integrity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The functionality of an NFT lies in the metadata describing the individual item it represents. That metadata can consist of traits describing the characteristics of a jpeg artwork, essential information about copyright and intellectual property, guidelines for the item’s intended presentation, or all of the above and more. Metadata can also address a broad set of questions:" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": " Am I allowed to use an NFT for commercial purposes based on IP and copyright? Can I play a specific video, given its file format and codec? Are there readable methods for rendering this generative work? Can I easily list this NFT on the larger NFT marketplaces, where I have the best chance of selling it for the best price and in a timely manner?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The rapid growth of the NFT market has further expanded the possible functions of metadata. Developers need an efficient way to retrieve all types of metadata, and to maintain correlation with their on-chain counterparts in provable, hash-linked data structures. Laconic Watchers—APIs that serve data from the Laconic Network—fill this need. The Watchers’ custom search and caching services collect variously constructed data and combine it into a unified form that DApps can interpret and use, without sacrificing data integrity." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "2. NFT data is fragmented and scattered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s not practical to use current blockchain technology as a data storage or retrieval protocol. It was designed primarily as a means for achieving trustless consensus, not as a data availability system. The assets to which NFTs refer are often stored in protocols such as " + }, + { + "url": "https://www.arweave.org/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Arweave" + } + ] + }, + { + "type": "span", + "value": " and " + }, + { + "url": "https://ipfs.tech/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "IPFS" + } + ] + }, + { + "type": "span", + "value": ", and pointed to by the NFT’s “" + }, + { + "url": "https://docs.openzeppelin.com/contracts/2.x/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "tokenURI" + } + ] + }, + { + "type": "span", + "value": ".”  In one recent example, complete rendering libraries are stored as " + }, + { + "url": "https://twitter.com/dhof/status/1569509636587528195?s=20&t=KUYxKh-vG7qFaTtCvhRObA", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "compressed, on-chain data URIs" + } + ] + }, + { + "type": "span", + "value": "; smart contracts then access these libraries to render fully on-chain generative artworks." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A problem emerges, however, whenever a DApp needs to access any of this information. Methods for retrieval and DApp ingestion vary wildly depending on data type and location. And while RPC services can locate data—for a price—in most cases there’s no measurable way to ensure its integrity. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Despite being the most lightweight element of the Laconic Stack, the Laconic Watcher has the power to alleviate the proof issue, by preserving evidence of proof across data transformations while querying a far smaller subset of data than is typically required." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "3. For both NFTs and equivalent token formats, data types vary from blockchain to blockchain." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Most of today’s NFTs are based on Ethereum, and most commonly written in Solidity. But zoom out for a wider view of the possibilities for both token types and blockchains, and the problem space increases correspondingly. On Tezos, for instance, smart contracts are most often written in SmartPy or LIGO, with third-place Michelson being a common low-level, domain-specific language. On Solana, Rust, C, and C++ are commonly used to compose smart contracts (referred to in the Solana ecosystem as “programs).” It’s safe to say that we have more than a small naming, language, and methodology mess on our hands in the blockchain ecosystem!\n\nLet’s imagine a DApp that tracks all NFTs representing a certain kind of media—books, for example—to provide an index of on-chain literature. It would need the ability to accurately interpret smart contracts from each chain, across widely varying programming languages, syntax, and metadata standards." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network drastically simplifies this process, offering developers a unified view of data while allowing DApps to agnostically query data from multiple blockchains from a decentralized, content-addressable database." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "4. NFT games require lightning-fast retrieval and scalability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The volume of NFT transactions is expected to rise significantly with the " + }, + { + "url": "https://tiktok.immutable.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "current influx" + } + ] + }, + { + "type": "span", + "value": " of " + }, + { + "url": "https://about.instagram.com/blog/announcements/instagram-digital-collectibles", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "institutional Web 2 players" + } + ] + }, + { + "type": "span", + "value": " into the Web 3 ecosystem. The problem: As transaction volume and speed increase, data availability with censorship resistance and proof of integrity becomes increasingly unsustainable. And blockchain-based games alone are poised to send NFT transaction volumes to stratospheric heights, with more and more in-game events and transactions driving mounting network traffic. For example, the popular game " + }, + { + "url": "https://godsunchained.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Gods Unchained" + } + ] + }, + { + "type": "span", + "value": " is already generating " + }, + { + "url": "https://chainplay.gg/games/gods-unchained/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "significant transaction volume" + } + ] + }, + { + "type": "span", + "value": "—and it’s just one of countless on-chain games. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The growth of games and social apps using on-chain transactions continues to expose issues with Ethereum’s scalability and speed. The problem is compounded by the fact that most indexing services are typically a few blocks behind with updates—too far back to have DApps react to in-game events on time. And that leaves players holding the bag, subjected to unnecessarily clunky and unwieldy gaming experiences." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike traditional blockchain indexing services, the Laconic Network is equipped to provide up-to-date blockchain data with trivial delay, for scenarios in which real-time data retrieval is essential to user experience." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "5. Data retrieved from multiple locations is difficult to verify. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A fundamental promise of blockchain is verifiability. And while on-chain data is verifiable " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "per se" + }, + { + "type": "span", + "value": ", NFT data can be stored in any number of data repositories. Meanwhile, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "using" + }, + { + "type": "span", + "value": " that data requires intermediaries such as DNS system records, traditional web servers, and files stored in Web2 datacenters. Every one of these exposes the data to the possibility of censorship, manipulation by bad-faith actors, or simple disappearance. In such scenarios, associated NFT data is most often tied to a token via a ”tokenURI” that references the location of a JSON file containing token metadata. That file, in turn,  lives in one of these off-chain data storage protocols.\n\nTypical " + }, + { + "url": "https://ethereum.org/en/developers/docs/apis/json-rpc/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "RPC" + } + ] + }, + { + "type": "span", + "value": " providers can retrieve information about such data, then provide it to a client. A wallet client, for example, can find any media files associated with a particular NFT and display them. But this creates a trust bottleneck in the Web 3 ecosystem—requiring DApps and their users to trust the source of the data without proof. Solving this trust point is one of the primary goals of Laconic." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Today’s NFT ecosystem isn’t providing standards. Enter Laconic." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s clear that across blockchains, tokenization standards vary widely. Even with similar token types on the same chain, we see countless examples of how and where data is stored:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": " " + } + ] + }, + { + "item": "55644653", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nStay tuned for Part 2, where we’ll dive into more detail on how Laconic solves common implementation and integration challenges, offering both collectors and developers a far smoother NFT experience. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Got questions? Join us on Discord.\n" + } + ] + }, + { + "item": "55644654", + "type": "block" + } + ] + } + } + }, + "featured": false, + "id": "55644655", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "99 Problems But NFTs Ain’t One", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "99 Problems But NFTs Ain’t One" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "99 Problems But NFTs Ain’t One" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1664296930-nfts005-1.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1664296930-nfts005-1.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-09-27T16:47:13Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "what-is-a-proof", + "title": "What Is a Proof and Why Do You Need One?", + "date": "2022-09-20", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55512259", + "name": "Stefan Adolf" + }, + "image": { + "url": "/images/site_content/blogPost/what-is-a-proof.png" + }, + "content": { + "blocks": [ + { + "id": "55510966", + "title": "Merkle tree" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralized apps rely heavily on blockchain state to display current and historical values like an account's balance, its NFT holdings, or current exchange rates on DEXes. Projects like The Graph or Covalent make blockchain data accessible, chain indexers like Etherscan provide historical information about the chain's state, and RPC providers like Infura or Alchemy allow wallet extensions like Metamask to connect to their full-node infrastructure. However, unless you run your very own full-node you cannot prove that any value provided by these sources truly resembles the chain state they represent. In short: relying on external, centralized services imposes a trust risk. However, the way Ethereum stores data allows trusting the chain state data without trusting the provider itself, thanks to proofs. This is how it works." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Ethereum's storage model" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ethereum uses a tree-based storage model that contains each account's balance and the storage for contract-based accounts. All values are wrapped in a traversable hash trie structure - a " + }, + { + "url": "https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Patricia Merkle Trie" + } + ] + }, + { + "type": "span", + "value": " - that allows creating cryptographic hash proofs for each tree node. The state root hash of a block represents the complete state of all accounts and contracts at a given block height, aka the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "world" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "state" + }, + { + "type": "span", + "value": ". Block producers execute new transactions on their local state copy and bundle the resulting new state root hash and a list of all executed transactions into a new block." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Light Ethereum clients cannot verify the current chain's state since they're not keeping a full copy of the world state. Full nodes can rebuild the state tree by executing every transaction since genesis, and then constantly update it as new blocks arrive. To save storage space they only keep the most recent chain states and hence cannot respond to state queries from the past, i.e. they're not able to determine an account's balance older than - depending on the node's settings - 64 blocks. That's sufficient to create proofs about the current state, though." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cryptographic proofs on hash trees" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Merkle trees use hashes of their nodes' values as keys to guarantee their content integrity. To build a simple binary Merkle tree, a prover starts by computing a hash over a node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "L" + }, + { + "type": "span", + "value": "'s content " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": ". Next, they compute the hash over " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": " and the hash of a sibling content node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "K" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": ": " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(h(K)+h(L))" + }, + { + "type": "span", + "value": ". Using this hash as a key they create a new node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "KL" + }, + { + "type": "span", + "value": " with " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(K)" + }, + { + "type": "span", + "value": " and " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": " as children and find another node" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "IJ" + }, + { + "type": "span", + "value": " at the same tree level to create a new parent node that hashes all values of the underlying tree structure: " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(IJ + KL) = h(h(h(I) + h(J)) + h(h(K) + h(L)))" + }, + { + "type": "span", + "value": ". This process is repeated until all nodes are combined to a single root hash. Since each parent node keeps a hash of its children, it's impossible to change anything down the tree without affecting the tree's root node." + } + ] + }, + { + "item": "55510966", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove that a certain base value has been incorporated into a Merkle tree's root, one collects the hashes of sibling nodes at each level of the tree. A prover successively computes their hash sums to finally recreate the root hash, thereby proving the inclusion of the node's value." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A simple way of storing binary Merkle trees in a flat key/value database structure is with " + }, + { + "url": "https://en.wikipedia.org/wiki/Radix_tree", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "radix tries" + } + ] + }, + { + "type": "span", + "value": ". To store a node, one splits its hexadecimal hash key into its nibbles (the hex characters) and create subdirectories for each of them. A node with the key " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "0xcafe" + }, + { + "type": "span", + "value": " would end up in a folder structure like " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "c/a/f/e/value" + }, + { + "type": "span", + "value": ". Looking up a node requires traversing the directory structure along the key's nibbles down to the last nibble's directory where the node's value will be stored. While simple to understand this approach is far too inefficient to store large tree data structures like Ethereum's world state. The network instead uses Patricia Merkle Tries that add some complexity to the data structure by introducing branch-, extension-, leaf- and null-nodes but are way more efficient to store and traverse. Additionally, a Recursive Length Prefix (" + }, + { + "url": "https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "RLP" + } + ] + }, + { + "type": "span", + "value": ") encoding is used to serialize nested arrays and data structures." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Proving account state" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ethereum's state trie consists of a mapping between account addresses and their state, defined by their current " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "nonce" + }, + { + "type": "span", + "value": " and " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "balance" + }, + { + "type": "span", + "value": " and in the case of contract accounts a " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "storageRoot" + }, + { + "type": "span", + "value": " key that points to a position in the dedicated contract storage trie and their binary code hash. Just using a block's " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " one can look up an account's current state at that height using a full node's LevelDB (Geth's default storage database). Our examples are using an archive node of Ethereum's recently launched " + }, + { + "url": "https://sepolia.dev/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Sepolia testnet" + } + ] + }, + { + "type": "span", + "value": " because it's still small enough to be synced to a developer's machine." + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst {\n toBuffer,\n bufferToHex,\n keccak256,\n Account,\n} = require(\"ethereumjs-util\");\nconst Web3 = require(\"web3\");\n\nconst web3 = new Web3(\"http://127.0.0.1:8545\");\nconst db = new Level(\".ethereum/sepolia/geth/chaindata\");\n\nasync function getState(address, blockNumber) {\n const block = await web3.eth.getBlock(blockNumber);\n const trie = new SecureTrie(db, toBuffer(block.stateRoot));\n\n //retrieves the account node's *value*\n const rawValue = await trie.get(toBuffer(address));\n const account = Account.fromRlpSerializedAccount(rawValue);\n console.log(account);\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "getState(\"0xe127a39da6ea2d7b1979372ae973a20bab08a80a\", 1432400)" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "value": "yields:" + } + ] + }, + { + "code": "account Account {\n nonce: ,\n balance: ,\n stateRoot: ,\n codeHash: \n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The provided account is an EOA, hence the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "codeHash" + }, + { + "type": "span", + "value": " value corresponds to " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "keccak256([])" + }, + { + "type": "span", + "value": " (another expression of a null value) and " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " is the hash of an RLP encoded zero:" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "keccak256(rlp.encode(0))" + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To create a proof of the account node's value against a block's state root you walk down the patricia trie starting at the block's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " node, resolve the paths on the trie's branch and extension nodes according to the signposts expressed by the leaf node's nibbles until the wanted leaf node is reached. On the way we're keeping track of all nodes we see while traversing the path. More details about " + }, + { + "url": "https://github.com/ethereumjs/ethereumjs-monorepo/blob/cfd7b7754490b072a035cceaba59c3dfb517effd/packages/trie/src/trie/trie.ts#L154", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the traversal can be found here" + } + ] + }, + { + "type": "span", + "value": ". We're using only high level methods to show proof creation here:" + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst { toBuffer, keccak256 } = require(\"ethereumjs-util\");\n\nconst db = new Level(\".ethereum/sepolia/geth/chaindata\");\nconst trie = new SecureTrie(db, toBuffer(block.stateRoot));\n\n//create a proof by finding the path on our own:\nconst { node: accountNode, stack } = await trie.findPath(\n keccak256(toBuffer(address))\n);\nconst proof = stack.map((stackElem) => {\n return stackElem.serialize();\n});\n\n//or by using a trie's convenience method:\nconst proof = await SecureTrie.createProof(trie, toBuffer(address));\n\n//or by using the ethereum node's RPC interface\nconst { accountProof: proof } = await web3.eth.getProof(\n address,\n [0],\n block.number\n);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All three proofs contain the same array of RLP encoded proof nodes, with our account node at its end:" + } + ] + }, + { + "code": "[\n \"0xf90211a078400dd6bd6f6cc13cdde5b707bdb6b9802fbd11809cba8aab2ee72f6920bbf3a0d88879eb6f43ea2d9c07538601e041aa46d64c87df68296a552ad12f8e5bebc0a044d40ac93c1bce308feda36a7712a38d9508087f04e612b1411bc57f80fa77a8a0b92c5fc0dcbc4a7982563c3667b940117770965e5f447578e4bde4fefcbb6d5aa0bc555b313ac90177e117dc10917581dad616770625e0956feb0df75a4be2ce63a0bd19008ae54cf1af36e59d7c92c553693dd468a0ef86c89401d01fc84aa4b751a0ee549b7ab8dd9a565ee6507bb46b9033e57b3a07541a4085763207911633367ca05652bd80f5b970a09c2090c38b13afa1772f3d97422c9a16548acdf629c35113a0a2e78f2d0da2d7218535b94bd69c4fad4425ff3bb22304d722185cf99dac2596a0a9470e771ae9cec5535b057948e68c4c38d8b8c1c2d8ed180d8f5244c63e422ea0726d10a2a5c85b156105414e75591611d896b8aaebd7fc92535845445a8baa81a0fdfb5aa2c35136eab67c3539d3ba34648bb8542b57e745976d52bca263ab19d7a01eafa4a1f59d28009a03b24d9a11c878faa78fe7ef3ecc4ef8891a77fea3fa35a02c980b0a841d5ec1978ae63adf7ba2727ac125f792f545cb0146d8df46adbdbfa052b97e4692fd69b4bebc6ceca258032319b07aca2ee62b504285031670fbc073a02c415e41b6bf838a8b742123fda110703ffdedea8834bb5113ae26f81887668e80\",\n \"0xf90211a098e0b7071d2b8d890cca479dba9f5b697d639d70d51f0312c5b8a27afc31a23da060759f93f482f66b233480a4cd060372567752b9dae507045b266600ab9b290da0c35bb5b2aae7eb8bc75a9de125daab9caa7d6d39e403153c9b34e51dcde46f78a097a4c1ba8ac42378481754eac9306b62d3d176602098820cb87e3c7f4091734da074b2785ca9199d0c68e2fa6dd01dcc24791887616adf8043cda56651cff25e87a00937963d57bc119144ebeff9bb99dcbef11aed563fd41818aaf0de89d15364b3a000ba5def8acd7c1c2af85ef49bc5d144fa1ceef93e81e9e64ad6457ab92cb569a069cd4b3ac6b03b1928f50840f81086ff0c819d1cbd445db0e5c306cfce9c0728a0a78339d0b9405da5a7943a6ac97b9585767a6f9f0e481b5e197d8fd820ac1cfaa019e1319ade8f1c2254dcef0b68483240044bc6bee2ea8197de895eb427068b1aa0ef36884df92b5769e521c6c0feaead503a8d70d11f493b3e6c8170ac917ced92a079c1ffdaa2a8d4ddacac77f47b43f1553c634300d23a8003574ec90874547cbaa0de4f130f1fd52baf5229e3871394b6f0562c64576039c6c38d06c695b7d87b07a09668798efa8c1e9b860d9f4a49b65db83c22fefd92f703a5875b87c7ae11bdf4a087ca8f145dbc9ad7f88468dbec0627d77ced63a508b4d7a5d28439e89c6be8e4a04fdb75be4fce4d76e41578f0bbcfdc2ded74d59855c9f3afc73c3659b9e28aa980\",\n \"0xf90211a058d99773cf489bd86bac2c6452c20cc7056f3505d7f96d83480cf85336d0da85a07dc4216585f73a213fb1828076cf7d93d8ce95e1981a5c8eb11d7166af8433eba0f85f31a95a1f83516719ae596266afdc529aa4ff9af8f0f6c1d7da6751f07d70a0f1371490b8ef31ce814673e9ecf0f498f2ec9eedcd554e93162d5c0e99e77d69a01d9709211cc089eba15fe3c052539203d11235b219e1c72e0172c9bec1d317bea09e907ad084233149719b5c6f8b4777a617b41744a0c3e9f6ef002970465a0656a044f3abc34546455ba2ed3d64ead41c734d5f56c240f8f8adfd971179538ca0efa08403716832691c8867b1366421b703925ff28e45d6f4360f898b70461e29d4eda0d8f4711ffe7bc79040b2f397523786a67d72814e8af9b65c9e03c318bb0eba48a0db84d2a268e67d6e45af7cd18a92fab1df496180e9ba8f0f0b620ab6344457cba0d01707094316206d689d80ed2684cda3aadd72ee2d4efbe1437bb194aa0adc87a0355e0e203341ac995bf2d90937f1ce807a13624e9d3831476f3205838b84f191a083961a9b498b791ee8e6a110a87aca2f2cea629df0827f4fbd168567d7246207a05d312338764151dc1936485637367ad9d9c440b4b0dde368dec4dbd5aaf51f4ea076b77567690221701e2edfff06f1c84a25b3181e4c8aa3bed2b3e3797a883564a0975b63f1be5615a4a68ec7f313abc0784def11a352975c40e886e03df95f764e80\",\n \"0xf9013180a0944968a4a8c7ba1e91cbb2414471a67f16c74e2ef6ac87447af0402520aed9c88080a03f3e1fed1008e242b89ce5b56106a091f946826c539f23ecee74c5b36b5d38cca041b58c41b5435e10c9b9f6b1f1bdf3d5e0294dfcfbf59b7a8c80475249e1d9e6a0d0b42862dcb175e47bf17614bd251b17cdab2710ba13175fd9b8c17afdc0893ba0ff4dc7ee613a7805802e50d39c3c8d8b35db63ceda7371502c5d119eca0a7b1780a0cd328766d44f930edc3a32ae5fd2a791f0de4dbabe014c4be77fc63b4b427310a0c20a6a1812f18d0c446408f90b09d0b592a07c45d06e780b49c6ad68e7c92bc5a084688c931305a75246f9d527a07483cff6e9ffbe4746a22ca65e07d3b3c6045f8080a0515f82b6e34b4713edead2652f266fdcea3729d64217113db564e918a9b1ed408080\",\n \"0xf8518080a06b91f274ef06ab455966416f6bb3779519bf72c68e85ec4b61ff8b99aa73a1818080a0937ee4540dc495eaa3cd61e12a4f6158a6be147d71ac3955a50ecd080e9732698080808080808080808080\",\n \"0xf8709e3e0484bbc22108bc77412e65255ce0387dc7c6a8d1917625ddb60ccbd98fb84ff84d028901f3d52b3c4f92e45da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"\n]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To verify this proof one doesn't need to have access to a full state tree at that block height. It's sufficient to know (and trust, e.g. by running a light node) the proof block's " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst { toBuffer, keccak256, Account } = require(\"ethereumjs-util\");\n\n// look Ma, no ethereum node needed\n// @param stateRoot hexString: the block's stateRoot\nasync function verifyAccountProof(address, proof, stateRoot) {\n const proofBufs = proof.map((p) => toBuffer(p));\n //build a new trie using only the proof nodes. They will hash towards the block's stateRoot\n const proofTrie = await SecureTrie.fromProof(proofBufs);\n\n //the account node can also be retrieved from the partial trie:\n const accNodeRaw = await proofTrie.get(keccak256(toBuffer(address)));\n const account = Account.fromRlpSerializedAccount(accNodeRaw);\n\n console.log(\"proven value\", account);\n const valid = await proofTrie.checkRoot(toBuffer(stateRoot));\n //or: const valid = bufferToHex(proofTrie.root) == stateRoot;\n return valid;\n}", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Proving contract state" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Proofs over contract state are created accordingly. They are rooted at the contract account's own " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " member variable that points to the mutable root of the contract's storage trie. Storage slots are addressed by their position hash as outlined in the " + }, + { + "url": "https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html#mappings-and-dynamic-arrays", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Solidity documentation" + } + ] + }, + { + "type": "span", + "value": " and are stored as leaf nodes in the storage trie. To simplify this procedure, we're going to use " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "eth_getProof" + }, + { + "type": "span", + "value": " calls here as described in " + }, + { + "url": "https://eips.ethereum.org/EIPS/eip-1186", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "EIP-1186" + } + ] + }, + { + "type": "span", + "value": " and supported by all major chain node implementations and service providers. Lets prove " + }, + { + "url": "https://sepolia.etherscan.io/address/0xF492600AeD292b1B94A1ba0CD29fB6ed6d6ab872", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this contract" + } + ] + }, + { + "type": "span", + "value": "'s " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "greeting" + }, + { + "type": "span", + "value": " to be \"" + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "Hello, Hardhat!" + }, + { + "type": "span", + "value": "\" at Sepolia block number " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "1391140 " + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "pragma solidity ^0.8.0;\n\ncontract Greeter {\n string private greeting;\n\n constructor(string memory _greeting) {\n greeting = _greeting;\n }\n\n function setGreeting(string memory _greeting) public {\n greeting = _greeting;\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now, lets create a proof of its storage slot 0 (the string variable " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "greeting" + }, + { + "type": "span", + "value": " ):" + } + ] + }, + { + "code": "const { bufferToHex } = require(\"ethereumjs-util\");\nconst { SecureTrie } = require(\"merkle-patricia-tree\");\nconst Web3 = require(\"web3\");\n\nconst web3 = new Web3(\"http://127.0.0.1:8545\");\n\nconst proveContractStorage = async (blockNumber) => {\n const proof = await web3.eth.getProof(\n \"0xf492600aed292b1b94a1ba0cd29fb6ed6d6ab872\", //the contract's address on Sepolia\n [0], //the first storage slot\n blockNumber\n );\n console.log(proof.storageProof);\n const value = web3.utils.hexToAscii(proof.storageProof[0].value);\n console.log(value);\n\n const proofBufs = proof.storageProof[0].proof.map(toBuffer);\n const proofTrie = await SecureTrie.fromProof(proofBufs);\n const valid = bufferToHex(proofTrie.root) == proof.storageHash;\n console.log(valid);\n};", + "type": "code" + }, + { + "code": "[\n {\n key: '0x0',\n value: '0x48656c6c6f2c204861726468617421000000000000000000000000000000001e',\n proof: [\n '0xf8518080a0e2a22c03c4f21673563d55cbad16de4c4affc9a54c3eea063ce358ccd6d02c4c8080808080808080a0fc47ec7aea920817d850c758a8fd61ecc89b967b2fe8d9ec6d00feedeb0a7d658080808080',\n '0xf843a0390decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563a1a048656c6c6f2c204861726468617421000000000000000000000000000000001e'\n ]\n }\n]\nHello, Hardhat!\ntrue", + "type": "code" + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "A non trivial example" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Creating proofs over nested structures, dynamic types like strings, or deeply buried storage slots is slightly harder because you have to be familiar with Solidity's " + }, + { + "url": "https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "storage layout" + } + ] + }, + { + "type": "span", + "value": ". To demonstrate that, here is a non-trivial example to prove that Jimmy Fallon owned " + }, + { + "url": "https://opensea.io/assets/ethereum/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d/599", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Bored Ape #599" + } + ] + }, + { + "type": "span", + "value": " at block height " + }, + { + "url": "https://etherscan.io/block/13572667", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "13572667" + } + ] + }, + { + "type": "span", + "value": " as he claimed during his Tonight Show " + }, + { + "url": "https://www.youtube.com/watch?v=5zi12wrh5So&t=222s", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "in January 22" + } + ] + }, + { + "type": "span", + "value": ". The Bored Apes NFT contract inherits from OpenZeppelin's legacy V3 " + }, + { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.2/contracts/token/ERC721/ERC721.sol#L36", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "enumerable ERC721 contract" + } + ] + }, + { + "type": "span", + "value": ". The data structure storing ownership information about a single token is the private " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "_tokenOwners" + }, + { + "type": "span", + "value": " member struct that maps a token id to an index onto another dynamic array of key value mappings." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove the ownership storage values we need to take a look at the contract's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "ownerOf" + }, + { + "type": "span", + "value": " implementation and its related structs. Here's a summary of the relevant parts of the " + }, + { + "url": "https://etherscan.deth.net/address/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d#code", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "full BAYC code base" + } + ] + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "library EnumerableMap {\n struct MapEntry {\n bytes32 _key;\n bytes32 _value;\n }\n\n struct Map {\n MapEntry[] _entries;\n // Position of the entry defined by a key in the `entries` array, plus 1\n // because index 0 means a key is not in the map.\n mapping (bytes32 => uint256) _indexes;\n }\n\n struct UintToAddressMap {\n Map _inner;\n }\n\n function _get(Map storage map, bytes32 key) private view returns (bytes32) {\n uint256 keyIndex = map._indexes[key];\n require(keyIndex != 0, \"EnumerableMap: nonexistent key\"); // Equivalent to contains(map, key)\n return map._entries[keyIndex - 1]._value; // All indexes are 1-based\n }\n\n function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {\n return address(uint160(uint256(_get(map._inner, bytes32(key)))));\n }\n //...\n}\n\ncontract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {\n\n // Mapping from holder address to their (enumerable) set of owned tokens\n mapping (address => EnumerableSet.UintSet) private _holderTokens;\n\n // Enumerable mapping from token ids to their owners\n EnumerableMap.UintToAddressMap private _tokenOwners;\n\n function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n return _tokenOwners.get(tokenId, \"ERC721: owner query for nonexistent token\");\n }\n //...\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The index mapping " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "_tokenOwners" + }, + { + "type": "span", + "value": " is the contract's third storage member (the 1st one being part of the ERC-165 implementation) and it implicitly points to the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "Map" + }, + { + "type": "span", + "value": " struct via " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "UintToAddressMap.inner " + }, + { + "type": "span", + "value": ", occupying two storage slots. By applying Solidity's " + }, + { + "url": "https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "layout rules for dynamic arrays" + } + ] + }, + { + "type": "span", + "value": ", the storage slot's address of the enumeration index that points to the current owner of token #599 can be computed as " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "const indexSlot = web3.utils.soliditySha3(599, 3);" + }, + { + "type": "span", + "value": ". Querying that storage slot's value by " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "web3.eth.getStorageAt(baycAddress, indexSlot, blockNumber)" + }, + { + "type": "span", + "value": " yields the index " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "0x258" + }, + { + "type": "span", + "value": " at the given block height. Considering that the " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "Map._entries" + }, + { + "type": "span", + "value": " array starts at the contract's third slot, it takes two slots to store one " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "MapEntry" + }, + { + "type": "span", + "value": " and the indexes are 1-based we can resolve the map's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "_value" + }, + { + "type": "span", + "value": " member that carries the token owner's address like so: " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "code" + ], + "value": "soliditySha3(2) + (0x258 * 2) - 2 + 1" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With that we can construct two storage proofs for the index and the owner's address:" + } + ] + }, + { + "code": "const baycAddress = \"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d\";\nconst blockNumber = 13572667;\n\nconst indexSlot = web3.utils.soliditySha3(599, 3);\n// -> 0x7e2616eb7a75f68a32624f502cf2cabc166c302900bbdc790c2fb85cea316a21\nconst indexValue = await web3.eth.getStorageAt(\n baycAddress,\n indexSlot,\n blockNumber\n); //0x258\n\nconst bnIndex = ethers.BigNumber.from(indexValue);\nconst valueSlot = ethers.BigNumber.from(web3.utils.soliditySha3(2))\n .add(bnIndex.mul(2))\n .sub(2)\n .add(1);\n// -> 0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5f7d\n\nconst proof = await web3.eth.getProof(\n baycAddress,\n [indexSlot, valueSlot],\n blockNumber\n);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove the " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " of the yielded proof, we can:" + } + ] + }, + { + "code": "async function validateStorage(proof, slot, proofIdx) {\n const proofBufs = proof.storageProof[proofIdx].proof.map(toBuffer);\n const pTrie = await SecureTrie.fromProof(proofBufs);\n const valid = pTrie.checkRoot(toBuffer(proof.storageHash));\n const rlpNode = await pTrie.get(toBuffer(web3.utils.keccak256(slot)));\n console.log(\"content at slot\", slot, bufferToHex(rlp.decode(rlpNode)));\n return valid;\n}\n\nconsole.log(await validateStorage(proof, indexSlot, 0));\nconsole.log(await validateStorage(proof, valueSlot.toHexString(), 1));", + "type": "code" + }, + { + "code": "content at slot 0x9f82913e56c1ea296cd5a3c46bc89a4073098f41767359e4c3742445923985c7 0x0258\ntrue\ncontent at slot 0xd4790f3899b463e8194660196b97cf3ff9c47008d83ca7c0e51ce406d3c784e5 \\\n0x0394451c1238cec1e825229e692aa9e428c107d8 (<- Jimmy Fallon's address)\ntrue", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Validating proofs inside smart contracts" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These have been client side examples, but proofs can also be validated inside Solidity contracts, e.g. to prove historical chain information that's not available to the contract itself. A good example can be seen at Lido Finance's " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "trustless ETH/stETH price pool oracles" + } + ] + }, + { + "type": "span", + "value": " that receives price reports as a combination of block header, account and state proofs. Since proofs are submitted " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle/blob/main/contracts/StableSwapStateOracle.sol#L295", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "as memory variables" + } + ] + }, + { + "type": "span", + "value": ", price updates " + }, + { + "url": "https://etherscan.io/tx/0xb24e20813e08f75e12e29da53f7f6c7e5dca7be68f3d3247edd4de572c527df4", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "are relatively cheap" + } + ] + }, + { + "type": "span", + "value": ". Here's a contract that's built on " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle/tree/main/contracts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Lido's primitives" + } + ] + }, + { + "type": "span", + "value": " and verifies any given state proof. It's also " + }, + { + "url": "https://goerli.etherscan.io/address/0x52e357f616a13089435be73e20cffb788eb4c928#code", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "deployed on Görli" + } + ] + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\n\nimport \"./StateProofVerifier.sol\";\nimport {RLPReader} from \"solidity-rlp/contracts/RLPReader.sol\";\n\ncontract ProofVerifier {\n using RLPReader for bytes;\n using RLPReader for RLPReader.RLPItem;\n using StateProofVerifier for StateProofVerifier.Account;\n\n /*\n struct Account {\n bool exists;\n uint256 nonce;\n uint256 balance;\n bytes32 storageRoot;\n bytes32 codeHash;\n }\n\n struct SlotValue {\n bool exists;\n uint256 value;\n }\n */\n\n function extractAccountFromProof(\n address _address,\n bytes32 _stateRootHash,\n bytes memory _proofRlpBytes\n ) public pure returns (StateProofVerifier.Account memory account) {\n RLPReader.RLPItem[] memory proofs = _proofRlpBytes.toRlpItem().toList();\n bytes32 addressHash = keccak256(abi.encodePacked(_address));\n\n account = StateProofVerifier.extractAccountFromProof(\n addressHash,\n _stateRootHash,\n proofs\n );\n }\n\n function extractSlotValueFromProof(\n bytes32 _slotHash,\n bytes32 _storageRootHash,\n bytes memory _proofRlpBytes\n ) public pure returns (StateProofVerifier.SlotValue memory slotValue) {\n RLPReader.RLPItem[] memory proofs = _proofRlpBytes.toRlpItem().toList();\n slotValue = StateProofVerifier.extractSlotValueFromProof(\n _slotHash,\n _storageRootHash,\n proofs\n );\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Continuing with the proof of Jimmy Fallon's Bored Ape, this is how you would prepare RLP encoded versions of the proof's node array as required by the contract's method interface:" + } + ] + }, + { + "code": "//converts an array of rlp encoded proofs to an rlp encoded array of proofs.\nconst rlpEncodeProof = (proof) => {\n const rlpDecodedProofs = proof.map((p) => rlp.decode(toBuffer(p)));\n return rlp.encode(rlpDecodedProofs);\n};\n\nconst rlpAccountProof = rlpEncodeProof(proof.accountProof);\nconst indexStorageProof = rlpEncodeProof(proof.storageProof[0].proof);\nconst valueStorageProof = rlpEncodeProof(proof.storageProof[1].proof);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and submit it to the contract:" + } + ] + }, + { + "code": "//state root of mainnet block #13572667\nconst blockStateRoot =\n \"0xa710dad6c716e0b762a671865cbe0d286f158198580f4ac97c4ace95ea85ba1b\";\nconst baycAddress = \"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d\";\n\nasync function main() {\n const Verifier = await ethers.getContractFactory(\"ProofVerifier\");\n // 0x52E357F616a13089435bE73E20CFfB788Eb4C928 on Görli:\n const verifier = Verifier.attach(process.env.CONTRACT_VERIFIER);\n\n //\"account\" is the bored apes contract\n const accountResult = await verifier.extractAccountFromProof(\n baycAddress,\n blockStateRoot,\n rlpAccountProof\n );\n const indexResult = await verifier.extractSlotValueFromProof(\n ethers.utils.keccak256(indexSlot),\n accountResult.storageRoot,\n rlpIndexProof\n );\n const valueResult = await verifier.extractSlotValueFromProof(\n ethers.utils.keccak256(valueSlot),\n accountResult.storageRoot,\n rlpValueProof\n );\n\n console.log(\n accountResult,\n indexResult.value.toHexString(),\n valueResult.value.toHexString()\n );\n}", + "type": "code" + }, + { + "code": "[\n exists: true,\n nonce: BigNumber { value: \"1\" },\n balance: BigNumber { value: \"0\" },\n storageRoot: '0x3f99b7df7989c11417c18b517c333ec74104e23ae76a50d578292ba3d466d77d',\n codeHash: '0x0ba5e25e74d81bab327110c8d8b44320f50ad5c3e91a546a5c5a9b605cf653b3'\n]\n0x0258\n0x0394451c1238cec1e825229e692aa9e428c107d8 // <- Jimmy Fallon, again.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Creating " + }, + { + "url": "https://eips.ethereum.org/EIPS/eip-1186", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "EIP-1186" + } + ] + }, + { + "type": "span", + "value": " compatible historical cryptographic proofs as demonstrated is a rather demanding task. It requires provers to traverse their archive nodes' LevelDBs, requiring up to 16 disk operations per account and storage slot. However, since state proofs like Jimmy Fallon's Bored Ape ownership at block height 13572667 are deterministic and valid forever, one could presciently collect all of the hashes along the nodes from root of the state tree down to specific nodes and index them. Chain indexers or RPC relayers like Metamask could use those hashes to execute proofs, thus minimizing trust in the service itself: each reply would be provable by the client." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network greatly simplifies the process of generating proofs for blockchain data, saving time for developers, and avoiding the extensive recursive querying of full-node databases that would otherwise be required." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "55510965", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "What Is a Proof and Why Do You Need One?", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "What Is a Proof and Why Do You Need One?" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "What Is a Proof and Why Do You Need One?" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1663659141-laconic_proof_blog_2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1663659141-laconic_proof_blog_2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-09-20T08:01:23Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "rick-dudley-on-interchain-fm", + "title": "Rick Dudley Discusses Laconic Network on Interchain.fm", + "date": "2022-09-13", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "/images/site_content/blogPost/rick-dudley-on-interchain-fm.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick Dudley, the co-founder of " + }, + { + "url": "https://laconic.com", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the Laconic Network" + } + ] + }, + { + "type": "span", + "value": ", talks to Chjango Unchained on a special live-streamed episode of " + }, + { + "url": "https://www.youtube.com/watch?v=viE0BZGx_DU", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Interchain.FM" + } + ] + }, + { + "type": "span", + "value": " about the risks of monopolization, why DApps have a hard time querying Ethereum data, how the Laconic Network solves this problem, and the legal aspects of creating a truly decentralized DApp data marketplace." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "How Laconic preserves verifiability" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is a data indexing service with an Ethereum L2 rollup that uses the Cosmos SDK and a fork of Ethermint. “Primarily, what we're doing is making third-party verifiable caches and indexes of Ethereum data, and we're building a marketplace to facilitate the buying and selling of that data.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For Ethereum-based apps, developers only need a subset of the Ethereum state to run their DApp. Laconic connects you with service providers who can serve that data to DApps, all in a decentralized, disintermediated way." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike other DApp data providers, Laconic focuses on the verifiability of the provided data. Rick points out that “there is no other platform that is preserving the verifiability in the way that we are.” Achieving this verifiability is anything but easy. If a DApp wants to run verifications using an archive node, it would take months of moderate computing time to do that, not to mention archival storage. “Most people don't have 14 terabytes just laying around that they can attach to their phone or laptop.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s answer is to split the data and decentralize validation. As Rick explains, “we take the whole Ethereum blockchain. And then we get a bunch of validators together and make you a little mini proof-of-stake blockchain of just the information you care about.” Ethereum remains the L1 in this scenario; “all the value that's transacted on the Laconic Network is actually staked or escrowed on Ethereum.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Leveraging linked data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic solution builds upon an established data format (IPLD, or Interplanetary Linked Data) to deliver the promise of verifiable data. The IPLD format not only allows transforming data to better serve the needs of DApps, but also makes it possible to verify these transformations. “It's not magic, but it makes it easier for us to deal with these very complex and robust data types. It also allows us to do cross-chain proofs, which is the main appeal.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only cross-chain proofs, but proofs about Ethereum events—which is not possible in Ethereum directly. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "“Events were never intended to be provable there. They weren't supposed to be used in the way they're used today.”  Rick explains how difficult it is for the average DApp developer to know that a single event really came from Ethereum, “You have to have the hundreds or thousands of events that came with that event, or you have to rerun the transaction in the block and see that it emitted the event. And both of those things are extremely expensive.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic has a technique for saving the effort of doing that re-computation, and IPLD plays a large part in that." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Acting lawfully" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network will have up to 67 validators that will be located in different countries. This raises some legal questions. Chjango points to a scenario where sanctions against countries can break the promise of decentralization. Rick explains how Laconic’s legal structure addresses this issue. Laconic is run by an LLC that is designed to “comply with the laws within its jurisdiction and to allow each member to comply with the laws within their jurisdiction.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If a country imposes sanctions on another country, the members are free to follow their local law and refuse service to members of the sanctioned countries. At the same time, the rest of the Laconic Network remains available for those accounts. All this can be done without sacrificing privacy. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "“You can use encryption and other things for someone to say ‘is this user in the United States, yes or no,’ without revealing where the user actually is.” Rick concludes that, “of the 67 members, we hope to have members that are willing to and legally able to service customers in those jurisdictions that are currently underserved.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Blockchain decentralization, or the lack thereof" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization in conjunction with global distribution is key to providing uninterrupted service in the face of ill-intended governments or political disturbance. Rick considers decentralization to be retreating, giving way to new monopolies. In other words, there is a re-centralizing effect." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The problem starts with the cost of verification. Chjango asserts that “the difficulty of fully verifying an Ethereum blockchain is such that it has hurt its decentralization, even though the underlying blockchain is decentralized.” The expense of running a fully validating node has introduced new centralization choke points." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick places the problem one level down, at the network. The internet comprises 60,000 sub-networks that communicate over the Border Gate Protocol (BCP). But a traffic analysis found Ethereum network traffic on only 18 subnets. And a handful of companies – AWS, Google, Microsoft, Cloudflare –  run much of the infrastructure, and are also in the same legal jurisdiction. So a single company can switch off large parts of the Ethereum system." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This lack of decentralization continues at the blockchain level. “There are 4000 validating nodes on the network, but about 80% of them are in AWS. And somehow, in spite of that, 50% of the blocks still come from three people.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic's answer to this problem is “forced decentralization.” Rick stated, ”Laconic requires its members to run their own hardware and to be located in different data centers. And we may at some point require people to run their own networks on the internet, so that we do maintain decentralization at that level.” Here, the legal side plays an important role.  The LLC can positively assert that a Member is a specific legally registered entity. They signed a contract that stipulates they avoid being in the same data center as another Member, or else they're in breach of the contract and they risk being removed from the network. \"That's a big benefit of the LLC, and definitely something that we've spent time thinking about to design it right,\" Rick continued." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Monopolistic forces are unavoidable. Give people an option to exit a toxic monopoly" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Even with Laconic’s solution, scalability and sustainability of the whole blockchain ecosystem are not a given. As pointed out earlier, more than 50% of Ethereum blocks are created by only three mining pools." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "(Source: " + }, + { + "url": "https://etherscan.io/stat/miner?blocktype=blocks", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Etherscan.io" + } + ] + }, + { + "type": "span", + "value": ")" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Such monopolization tendencies are hard to fight. Rick’s view is to initially accept the inevitable. “I think it's more about acknowledging that these things are going to be monopolized. And then building technology that allows us to exit those monopolies as needed, more than trying to build technology that's going to resist the monopoly.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The aim of Laconic is to make it trivial for people to exit when those systems get monopolized. To achieve this, Laconic has a few layers of protection in place to stop the Laconic Network from being compromised by monopolization attempts. As an example, the legal agreements prevent Laconic members from sharing investors. So an investor cannot simply invest in over one-third of the members to take it over." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But if some sort of monopolization does occur, every DApp user has an option to exit. “If every user of a DApp has their own Watcher (Laconic’s API servers) running, they would have all the state they needed to move that DApp to another chain. They could take that one Watcher, generate a new state snapshot for that application, and then move that state snapshot to a new chain. And that literally would take a year of processing on Ethereum." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What’s in the name Laconic?" + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Why “Laconic”? " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick explains, “It means terse.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Referencing the ancient inhabitants of Laconia known for their more concise and terse style of speech, Laconic also describes a style of speaking or writing that uses only a few words, often to express complex thoughts and ideas." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s “laconic” technology  elegantly simplifies the complexity of blockchain, and accelerates DApp development." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Takeaways" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This was a wide-ranging interview with deep-dives on many topics not covered here. Dive in to find out more about:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Payment channels and validator accountability." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Processing time and why they don’t discuss network speed or TPS at Laconic." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Why Laconic chose Geth over other Ethereum clients." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Catch the full interview on " + }, + { + "url": "https://www.youtube.com/watch?v=viE0BZGx_DU", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "YouTube" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thanks to " + }, + { + "url": "https://www.youtube.com/channel/UCMcYl850ni93ZDqchYF7v1w", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Chango Unchained" + } + ] + }, + { + "type": "span", + "value": " and Interchain.FM for leading this thought-provoking interview with Rick. It’s exciting to be able to showcase the work happening at Laconic, designing and implementing a solution for truly decentralized delivery of provable data from the Ethereum blockchain." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "55457818", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Rick Dudley Discusses Laconic Network on Interchain.fm", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Rick Dudley Discusses Laconic Network on Interchain.fm" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Rick Dudley Discusses Laconic Network on Interchain.fm" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1663008114-chjango.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1663008114-chjango.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-09-13T15:05:48Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "laconic-watchers", + "title": "Laconic Watchers: Ensuring Trustlessness in Web3", + "date": "2022-09-06", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/laconic-watchers.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Watchers in the Laconic Network are the keystone technology that will scale blockchain data access for the next wave of Web3 adoption. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic stack provides a way for DApp developers to retrieve the blockchain data they require in a manner that is efficient, verifiable, and decentralized. The mechanism by which the relevant subset of blockchain data is queried, cached, and delivered, is the Laconic Watcher." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve previously explored " + }, + { + "url": "https://www.laconic.com/blog/how-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "how the Laconic Network uses IPLD" + } + ] + }, + { + "type": "span", + "value": " (“the data model of the content-addressable web”) to keep off-chain data cryptographically verifiable across transformations. The Laconic Watcher is the component that makes that data available for DApps.  You will learn about the role of Watchers within the Laconic Network, how they work, and what you can use a Watcher for." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "DApp Developer Challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A public, permissionless blockchain is optimized for storing large amounts of data and achieving consensus. A DApp typically needs only a tiny fraction of that data for its operations. Ethereum clients are not designed to serve the data needs of a given DApp, such as reading and linking data from multiple contracts, introducing cross-chain data, or presenting DApp-specific information to users. Existing options for retrieving blockchain data have some drawbacks:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Significant effort required: " + }, + { + "type": "span", + "value": "Running an archive node gives access to all of a blockchain’s data.  Structurally, blockchains are write rather than read optimized. Many developers are unable to, unwilling to, or cost-prohibited from, running the infrastructure." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Trustlessness broken: " + }, + { + "type": "span", + "value": "Centralized services can provide indexed blockchain data, but  they lack the mechanisms to cryptographically prove their accuracy and provenance. DApp developers and users must trust these services, contrary to the decentralized and trustless promises of Web3." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our goal is to make DApp development as frictionless as possible. When you build your DApps, Laconic Watchers relieve you of the infrastructure burden and enable you to query and receive trustless, verifiable, off-chain blockchain data." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Watcher and the Laconic Network" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To understand the Watcher’s role and functionality, let's briefly recap how the Laconic solution works." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The primary function of the Laconic Network indexing is to keep an up-to-date version of blockchain data in a relational database. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To achieve this, a Laconic Full Index Node fetches the latest blockchain updates and turns them into IPLD blocks with advanced indexing. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike existing ETL (Extract-Transform-Load) solutions that convert blockchain data into a searchable database format, the Laconic Network continuously monitors and evaluates state diffs on the chain, retrieving and indexing relevant tries." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This additional data opens up a new range of opportunities for DApps. This is the first component of the Laconic solution.  However, the database is still too large and too general for DApps to use; raw blockchain data structures are not suitable for DApps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Enter the Laconic Watcher." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Your DApp’s Personal Data Delivery Service" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Watchers are the component that makes DApp development as frictionless as possible. Watchers serve three fundamental purposes:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Query" + }, + { + "type": "span", + "value": " the Laconic Full Index Node for the specific data your DApp needs." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Transform" + }, + { + "type": "span", + "value": " the queried data to make it consumable for your DApp." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cache" + }, + { + "type": "span", + "value": " the data for fast and inexpensive access." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Watchers run queries relevant to your DApp through a GraphQL interface; you can create precisely the API you need, transforming the data as required. Watchers run as daemons, constantly updating the cache with transactions from the blockchain's head." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your Watcher is effectively your personal, virtual, read-only blockchain, containing the specific data your DApp needs. You get the same verifiability you would from the source blockchain, without the same burden or overhead." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Where do I find Watchers? How can I write one? Do I have to?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Running Watchers: " + }, + { + "type": "span", + "value": "It is the job of Service Providers on the Laconic Network to run Watchers. One or more Service Providers can run a given Watcher." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Do I have to write my own Watcher?" + }, + { + "type": "span", + "value": " As a DApp developer, you don’t necessarily need to create your own Watcher. Some alternatives:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Pay to access a public Watcher API. " + }, + { + "type": "span", + "value": "The fees are split between the Service Provider and the Watcher creator." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Check the Watcher Registry:" + }, + { + "type": "span", + "value": " If a Watcher’s creator chooses to make it publically available, they can add the Watcher to the on-chain Watcher Registry for easy discovery. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "You don’t have to start from scratch. " + }, + { + "type": "span", + "value": "Watchers are composable. You can build a Watcher on top of one or more existing Watchers found in the Watcher Registry." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What about Smart Contracts? " + }, + { + "type": "span", + "value": "As a Smart Contract author, you can auto-generate Watcher code directly from your Smart Contract's Solidity source code. The code generator is capable of generating the Watcher GraphQL API based on eth_calls as well as storage variables in the Smart Contract." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Watchers, in Conclusion" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Watchers" + }, + { + "type": "span", + "value": " allow DApps to cache and query verifiable blockchain data served by a decentralized network of Service Providers. Watchers facilitate the creation of custom APIs suitable for a DApp’s specific needs. " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "As a developer," + }, + { + "type": "span", + "value": " you will benefit from the Laconic Network by finding, writing, or combining Watchers to serve the backend data needs of your DApps." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "As a Laconic Network Service Provider or Validator," + }, + { + "type": "span", + "value": " you will be part of solving the problems of blockchain data access for the next wave of Web3 adoption. " + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stay up-to-date with Laconic News" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Join our " + }, + { + "url": "https://discord.com/invite/ukhbBemyxY", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Discord server" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Subscribe to our " + }, + { + "url": "https://t.me/share/url?url=https%3A%2F%2Fwww.laconic.com%2Fblog%2Fhow-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Telegram channel" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Sign up for our mailing list below" + } + ] + } + ] + } + } + }, + "featured": true, + "id": "55448692", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Laconic Watchers: Ensuring Trustlessness in Web3", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Laconic Watchers: Ensuring Trustlessness in Web3" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Laconic Watchers: Ensuring Trustlessness in Web3" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1662426120-laconic-watchers.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1662426120-laconic-watchers.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-09-06T06:16:55Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "how-laconic-network-uses-ipld", + "title": "How Laconic Network Uses IPLD", + "date": "2022-08-30", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "7023106", + "name": "Christoph Berger" + }, + "image": { + "url": "/images/site_content/blogPost/how-laconic-network-uses-ipld.png" + }, + "content": { + "blocks": [ + { + "id": "55419640", + "title": "IPLD" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Accessing blockchain data off-chain is no longer a matter of trust" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The mission of the Laconic Network is to ensure that decentralized off-chain caches can serve blockchain data without losing the ability to prove the integrity of that data across data transformations. This post shows how the Laconic Stack maintains the verifiability of Ethereum data for its decentralized caching and querying solution. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Querying blockchain data is difficult and expensive" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "DApp developers face a dilemma when it comes to working with blockchain data. Ethereum data is stored in various ways, including the state, storage, and transaction tries, as well as in event logs. The original concept for accessing Ethereum data was that DApps would run or have access to an Ethereum full node and query it directly. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "However, DApp developers rarely want to run their own full node only to gain access to the data they need. Further, and more problematic, due to the way data is stored on Ethereum, there is often no direct way of querying for particular information. Because blockchains are optimized for writing new blocks and achieving consensus, they cannot be indexed like a classic database. As a result, to query for data, developers have to make multiple calls to the Ethereum API, replay transactions to read the events emitted, and store intermittent state in the application to join it with data from subsequent invocations.  This process is difficult and expensive." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The alternative to running a full node is to use a middleware indexing and caching service. While this alternative simplifies data acquisition for developers, it introduces some new and significant problems. In addition to high costs, long setup times, reliability issues, and the dependence on Web2-style centralized services, there is one problem that is especially pernicious: the data served by these services must be " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "trusted" + }, + { + "type": "span", + "value": ". It is currently impossible for existing centralized data providers to offer cryptographic proofs for every byte of data served." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Solution: Laconic Network and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Many people are familiar with the Interplanetary File System (IPFS), but fewer people know about the Interplanetary Linked Data (IPLD) protocol. IPLD is the underlying technology of IPFS, and it is the superpower technology behind many of IPFS’s advantages. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD can represent arbitrary data in self-describing structured objects, and it can link those objects into Merkle DAGs. As such, IPLD offers the following advantages:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is content-addressable via a Content ID (CID), just as IPFS files are content-addressable." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is cryptographically provable, meaning you can verify that a set of data belongs in a larger data set even without needing to have either set on hand. You only need the cryptographic hashes." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data undergoes natural deduplication, reducing storage and transport costs." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is tamper-proof. Any attempts to change the data that you have requested via content addressing can be foiled by validating the hashes." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD codecs map data from its original form to the IPLD data model. So if a codec exists for that data, the data has a formal representation in the IPLD data model. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Connection to Ethereum" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the case of Ethereum data, the " + }, + { + "url": "https://ipld.io/specs/codecs/dag-eth/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "DAG-ETH codecs" + } + ] + }, + { + "type": "span", + "value": " can be used to map on-chain data to off-chain IPLD structures. This mapping to IPLD makes it possible to inspect, process, or reason about data in a uniform way. The codecs cover block headers, uncles, transactions, transaction receipts, and receipt logs, as well as all the different Merkle Patricia tries that are rooted in an Ethereum block. As a result, IPLD can accurately represent any Ethereum data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike the original data, the off-chain IPLD representation enables indexing for fast querying. The restrictions of the blockchain do not apply here. Indexes can be added that allow developers to query data in ways that are otherwise difficult to achieve with the native ETH JSON RPC API. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "More importantly, the IPLD representation of blockchain data can be kept verifiable even after transforming the data into other models. Transformations are necessary to support complex DApps that aggregate and link data in app-specific ways or rely on a non-native representation of the data. The classic methods of slicing, dicing, and recombining the data would render it unverifiable. With Laconic Network data, thanks to our use of IPLD, the data remains as verifiable as it is on the blockchain." + } + ] + }, + { + "item": "55419640", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Merkleized data is provable data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The IPLD representation of an Ethereum block includes the block header and the uncles, transactions, receipts, logs, receipt trie, transaction trie, state trie, and storage tries that Ethereum stores alongside the block header. A modified Merkle Patricia Trie (MMPT), which is relevant to Ethereum, has branch nodes, extension nodes, and leaf nodes. Non-leaf nodes store the content hashes of their child nodes. This hashing goes all the way up to the root node. Therefore, the hash value in the tree root represents all leaf data. If any part of the data in a Merkle tree is tampered with, the root hash would change and differ from the root hash stored in the block header. In other words, data like state, transactions, and more, is connected to the block header in a provable way.  " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Storing all of the IPLD: Laconic Full Index Nodes" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Traditionally, Ethereum Full Nodes are used to power DApps that need to access and query all of the data of the blockchain. The disadvantages of relying on a normal Full Node are well known: " + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Expensive to operate" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not optimized for data querying" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Logs stored from events have to be replayed to access the data" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Full Index Nodes (LFIN) overcome these limitations by generating additional derivative data based on that which is stored in the Ethereum Full Node, adding new indexes, relational mappings, and materialized views, and even merkleizing some data that was previously not merkleized. The result is an amount of data that far exceeds what is normally stored. The role of the LFIN is to track and store the complete data, including derived indexes and Merkle trees of that data, in IPLD block format, for eventual consumption by DApps. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data representation inside of the LFIN optimizes many classes of queries by removing the need  to chain together data from multiple transactions. For example, listing all transactions for a particular wallet address could be achieved with a single query. Or gathering all of the node hashes needed to generate a cryptographic proof. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Laconic Watchers - interoperable and provable blockchain data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because the volume of data in the Laconic Full Index Node is so vast, most DApps would not want to have to query that database directly. Instead, DApps work with a specialized caching layer that exists to transform LFIN data into the format needed by specific DApps. These are Laconic Watchers. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data in the LFIN is still not ready for DApp developers at this point. Further transformation is needed to structure the data in the format required by your DApp. This is the role of the Laconic Watcher. Watchers will be the topic of a subsequent article, but within the scope of this article, you can understand them as being custom secondary or tertiary caches of data that directly fulfill the needs of specific applications. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thanks to our use of IPLD, these transformations can occur without losing the cryptographic integrity of the data. Developers can build their Watchers to track, process, and cache the underlying blockchain data, and then query the watchers using GraphQL. The returned data includes the linked hash structure that can be followed all the way back to the underlying blockchain. In the Laconic Network, when any data is being transformed, the hash of the inputs to the transformation and the hash of the code that performs the transformation are preserved. Thus:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "code" + ], + "value": "T(a+b) => c" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The new model, c, will contain content-hash references to a, b, and the code performing T." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One intentional consequence of our adoption of structured IPLD models for data representation is that it greatly simplifies dealing with cross chain data. Our ability to transform data while maintaining linked hashing will be invaluable when we begin to index chains beyond Ethereum. Laconic Network is designed with blockchain interoperability in mind." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The source of proof" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network improves the Web3/blockchain ecosystem greatly by combining the validation-preserving nature of IPLD with a new way of caching and transforming blockchain data in a decentralized manner. This ability frees DApp developers from relying on centralized, trust-based data providers and closes the cryptographic provability gap between the blockchain and DApps built to use blockchain data. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Stay up-to-date with Laconic news:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://discord.com/invite/ukhbBemyxY", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Join our Discord server" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://t.me/share/url?url=https%3A%2F%2Fwww.laconic.com%2Fblog%2Fhow-laconic-network-uses-ipld", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Subscribe to our Telegram channel" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Sign up for our mailing list below" + } + ] + } + ] + } + ] + } + ] + } + } + }, + "featured": true, + "id": "43258780", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "How Laconic Network Uses IPLD", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "How Laconic Network Uses IPLD" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "How Laconic Network Uses IPLD" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1661833374-ipld-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1661833374-ipld-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-08-31T00:13:42Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "introducing-laconic-network", + "title": "Introducing Laconic Network", + "date": "2022-07-26", + "category": [ + { + "id": "2965426", + "slug": "news", + "title": "News" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/introducing-laconic-network.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The advent of smart-contract blockchains, led by Ethereum in 2014, has given rise to exciting new possibilities for humans interacting and transacting with each other. The ensuing rise of Web3, with wider consumer adoption of DeFi and NFTs in particular, has begun to show the world how public blockchains that are permissionless and trustless can bring immense value to industries and individuals. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The value of blockchain:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Immutable data that brings transparency to finance and governance" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Efficiency through smart contract automation " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Security and privacy through emerging cryptography " + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Yet all of these benefits hinge on developers succeeding in writing Web3 applications on top of blockchains that are fast, efficient, secure, and frictionless to use. There will be no internet-scale adoption of Web3 if the user experience is bad, or if it is simply too difficult to write the applications in the first place. And this point highlights a critical weakness of blockchains - accessing the data that they store is not as simple and straightforward as one might expect. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the contrary, since blockchains are write-optimized databases that focus nearly exclusively on establishing consensus and finality of each new block, they are notoriously difficult to work with for data retrieval." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The result of this has been the rise of a middleware industry to index, cache, and serve blockchain data. This includes services like Infura, Alchemy, and The Graph. These services, while more convenient for developers to use when building DApps, all break the fundamental benefits of blockchain in one or more ways. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, they tend towards centralization, and we end up with an industry where the blockchains are decentralized, but the DApps built on them all pass through an oligarchy of service providers that are not materially different from the IaaS cloud providers that represent the hyper-centralization of Web2. Second, they serve data that can no longer be cryptographically proven to be the correct data, so the trustless benefit of blockchain technology is replaced with a trust relationship with a single company. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Until Laconic, there has not been any technology that serves blockchain data while truly upholding the ideals of decentralization and allowing all data to be independently cryptographically verified. Key components of the emerging Web3 ecosystem, like Metamask, use centralized APIs from OpenSea and Infura without any mechanism for cryptographically verifying the accuracy or provenance of the data. Yet, provable data and decentralization are essential to having truly trustless systems for Web3 to depend on." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It is precisely this service that Laconic has been created to deliver." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Blockchain Data for High-Performance Applications" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network solves these two fundamental and existential problems. We are creating a truly decentralized network of data indexers and API providers to deliver hash-linked (cryptographically provable) blockchain data off-chain. Every byte of data served to DApps via the Laconic Network can be verified and proven to be mathematically integral." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We are thrilled at the prospect that the Laconic Network will become the catalyst and enabler to unlock immense potential value in the blockchain. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the last five years, our team of platform experts across Ethereum, IPLD / IPFS, and Cosmos SDK has been leading research and development in this space. We are creating a new set of technologies and infrastructure to solve the fundamental problem of making blockchain data accessible to high performance, high availability applications. The work has included the application of IPLD block structures to preserve the hash-linked structure of blockchain data when stored in traditional relational databases and served via API endpoints." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "The upshot: No longer will DApp developers have to struggle with convoluted queries, poor performance, long indexing times, or unprovable data when building decentralized applications. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Enter the Laconic Network" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Furthermore, we’ve pioneered a novel organizational and governance structure that solves the seemingly incompatible goals of providing guaranteed data availability service levels via a truly decentralized network. Laconic Network Members are corporate entities who enter contractual obligations to offer data indexing and retrieval services to end-users with professional-grade performance guarantees. Yet the network of these partners is technically, legally, and even geographically decentralized, and therefore impervious to censorship or monopolistic control. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the primary responsibilities of Laconic Network Members is to run Laconic Watchers, which are programs that expose data APIs to DApps, maintaining specific views of blockchain state and data expressed as mini-blockchains, solely for the purpose of reliable, provable data querying. DApp developers can find and use existing Laconic Watchers and can programmatically create new Watchers. There will be token incentives for programmers who write Watchers. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, the Laconic Network features an incentivized data caching layer to ensure robustness.  All blockchain data can be made permanently, globally available through the caching layer, without needing to directly query blockchain nodes, and, of course, without sacrificing the cryptographic integrity of the data. People who care about decentralization and provable data will be able to participate in this caching layer and earn tokens while doing it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The upcoming launch of the Laconic Network will bring a new era marked by the positive value that blockchains bring to humanity, with ever more practical and impactful use cases emerging as the barriers of data availability fall away." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network will have opportunities and benefits for anyone who cares about the future of the decentralized web:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://discord.com/invite/ukhbBemyxY", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Join our Discord" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://t.me/laconicnetwork", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Chat with us on Telegram" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and " + }, + { + "url": "https://www.laconic.com/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "sign up for our Newsletter" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "How have others tried to solve these challenges?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The original vision of Ethereum was that DApp developers would connect directly with Ethereum nodes and use the JSON-RPC API of an Ethereum client to query data. This allows you to get data from a source that has verified the data, and is decentralized, but it requires you to run the node yourself, or hire someone to run it for you. Furthermore, the data is not indexed in such a way that is convenient for DApp developers, and a large number of queries and processing must be done to satisfy normal application cases." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To improve the convenience of querying Ethereum data, many indexers have taken the data and put it in off-chain databases, often with sensible indexes. They then expose this data over new APIs that foster rapid application development and developer ease, but completely forsake the first principle values of decentralization and cryptographic verifiability. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To our knowledge, no indexer has managed to provide the convenience of custom indexes and APIs while preserving decentralization and cryptographic proofs of the data integrity." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Laconic's Solution: How are we different?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Developed over four years by our team of Ethereum, IPLD, and Cosmos SDK core contributors, the Laconic Solution encompasses three main components:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Stack, a set of services which convert Ethereum data into IPLD blocks and populate our high-performance, carefully indexed databases.\n\n" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic SDK, which makes it easy for developers to write front ends and other services which consume IPLD-based data structures and proofs.\n\n" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network, a multi-sided marketplace that allows DApp developers and service providers to transact in a more cost-effective way and then pass those cost savings on to end-users, providing a sustainable and decentralized operating model for DApp developers. The key element of the Network is Watchers, custom materialized views of blockchain data that developers query to access specific blockchain data." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Stack consists of our modified Geth client which extracts and denormalizes the data, transforming it into IPLD blocks and indexing it properly for efficient querying and future transformations. For example, we can list all the node hashes needed to execute a cryptographic proof in one query, instead of having to walk down the Merkle tree with a chain of sequential queries. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The main contact point for developers is the Watcher, a materialized view of a specific subset of blockchain data (e.g. a specific contract or protocol) that provides a custom endpoint for DApps to query.  " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "$LNT, the Laconic Network Token" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With a fixed total supply, Laconic Network Token (LNT) serves multiple functions in the network design, most saliently by securing incentive alignment across network stakeholders. In addition to being used as rewards for service providers to run Watchers, LNT also allows for seamless transactions across services, regardless of blockchain or DApp." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Distribute rewards to staked users" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Provide service discounts" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Distribute funds to community members for Network maintenance and development" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "What’s Next for Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth mode, we’re looking forward to our public marketing debut this month. Later in Q3 2022, we will announce the initial cohort of seven Founding Members, as well as a funding round. As our technology, governance, and network are novel, with no current analogies, we're excited to share more articles about key aspects of the Laconic Stack and Laconic Network, including our use of IPLD, the role of Watchers, the features of the Laconic App, and our pioneering governance framework." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For those wishing to run validators, we're aiming to have an incentivized Testnet in 2023, followed by the launch of our Mainnet later in 2023." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Summary of Laconic Network Milestones" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Public Marketing Launch: Q3 2022" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Coming out of a long stealth period, we’ll be launching our marketing assets and channels, including branding, website, social media and developer platforms, and content." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Seven Founding Members and Funding: Q3 2022" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ll be announcing our initial seven Founding Members and the close of our seed funding round." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentivized Testnet: Early 2023" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With the launch of our Incentivized Testnet, prospective validators will be incentivized to learn about the requirements for being a Member/Validator on Laconic Network and how to operate the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Mainnet Launch & Incentivized Global Data Cache: Late 2023" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Once Laconic Mainnet launches, " + }, + { + "type": "span", + "value": "and 7 Member/validators have joined the network, we’ll have an incentivized global data cache, allowing anyone to easily participate in serving blockchain data to DApps (and earn $LNT in doing so)." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "24195830", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Introducing Laconic Network", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Introducing Laconic Network" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Introducing Laconic Network" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1659030103-introducing-laconic.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1659030103-introducing-laconic.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-08-30T04:19:58Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllPostsQueryResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllPostsQueryResult.json new file mode 100644 index 0000000..29eb06c --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/blogAllPostsQueryResult.json @@ -0,0 +1,12520 @@ +{ + "data": { + "allBlogPosts": [ + { + "slug": "rick-dudley-on-the-interop", + "title": "Rick Dudley Discusses Laconic Network on The Interop", + "date": "2023-02-09", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1675901476-laconic-seb-blog.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic cofounder Rick Dudley appeared on a special livestream of The Interop with host Sebastien Couture to discuss the Laconic Stack, the blockchain data problems that Laconic solves, Laconic’s novel governance structure, and how Laconic can index and verify data faster, more efficiently, and at lower cost. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Below is a distilled transcript of Rick’s responses during the discussion." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Future is App Chains" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think there will be millions of chains, and we'll be using a combination of rollups and mesh–not straight linear L1, L2, L3, but also meshes of rollups and attestations publishing bridges, etc. And although we may have millions of chains, we won't have millions of massive chains. A large chain may have 100 members, and there may be one or two chains out there with 4,000 validators. But in the world, you only need a few of those." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think everything becomes an app chain. I think mainnet Ethereum ultimately becomes an app chain and the application is settling rollups–very similar to Cosmos Hub, frankly. Polkadot, Ethereum 2.0, Cosmos Hub are all actually very similar in terms of the endgame state in the final thesis. And I don't think that there will necessarily be a winner per se. I think they will have curious different properties. " + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Why Laconic?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The ultimate goal of Laconic is to get all of the data that a user is concerned about in the hands of that user. Not in a cloud-hosted environment, not in Microsoft, not in AWS, but in users’ actual custody. And to enable them to do all the verification themselves." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Right now, it’s very difficult to extract parts of data from the Ethereum Mainnet that are relevant for Dapp needs. It’s almost impossible to synchronize a Geth node in a reasonable amount of time." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are multiple light client protocols that have come around to help alleviate this problem but they still don't go all the way. The Laconic Network goes the whole way. It goes from source code, to what is in the user's eyeballs with everything being verifiable. If you see a message on Laconic that came to you through the Laconic Network, you could say, \"I want to know which blockchain or blockchains this came from. I want to know what code generated this result. I want to know who wrote that code.” We provide all of that in the Laconic Network." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Three Major Components of Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is the Laconic LLC itself, which is in the Cayman Islands. There is the Laconic Stack, which is the standalone software that anyone can run today to generate this data and the evidence that they need. And then there's the Laconic Network, which facilitates the buying and selling of data. It facilitates running these services, discovering the services, paying for services, and then making sure all of that is verifiable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Those three components are an evolution. We've iterated on the Stack many times over at this point. MakerDAO is still using an early version of that stack to this day last I checked, which was recently. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you were an intrepid developer, you could go into the Stack Orchestrator code and run that yourself and put that into production yourself right now. But the problem with that is it's very expensive to generate this evidence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s computationally very expensive, and specifically, disk I/O operations are very expensive activities to do. So as a Dapp developer, when you have very few users, you can run this reasonably on the laptop. But as your app grows, or if you're wanting to see all of the Uniswap V3 pool data, then a laptop's not going to be able to process that in a timely manner necessarily. I mean, laptops are pretty powerful so some of them can, but maybe not all of them. And at that point, you need hardware. And when you need hardware, you then have this problem of, \"Okay, well am I going to buy hardware and rack it in a data center?\" That's probably not a viable answer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Am I going to go to AWS? Well, AWS is centralized, there are all sorts of problems. There's censorship for instance. AWS may choose to comply with a law that I'm not legally obligated to comply with. We've seen this issue with Alchemy and Infura, and these solutions comply with the laws in their jurisdiction, but the Dapp developer is in a different jurisdiction. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So then you end up with this situation; \"Okay, well if I want to have multiple service providers actually serving this data to users, they need to be in multiple jurisdictions.\" And that's what Laconic LLC solves. It's a Cayman Island LLC. We have members in different jurisdictions and those members will contract with the end users and comply with those laws in that way." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and Cosmos" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic team members were also core contributors to Cosmos SDK–we did a bunch of work on the Cosmos SDK. The data structures in Ethereum and the data structures in Cosmos and many other blockchains were designed to facilitate consensus, not to facilitate reading the data back out. And so in those architectures, there's utility in taking the techniques that we've applied to Ethereum and applying them to those other chains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is a value and utility to taking those techniques and applying them to the Cosmos SDK chains. Osmosis is an example of where it would be useful. For example, you can't have a block explorer that works across Cosmos Hub upgrades. No one's ever bothered to build one that works that way. If you built the block explorer on top of Laconic instead of directly on top of the chain, you would actually be able to provide that continuity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Every time a Cosmos chain upgrades, they regenesis and restart the chain. When you start that new genesis, people–just as a matter of convenience–don't preserve that data. You don't have a way of representing the irregular state change that happens during the upgrade. Whereas in the Laconic system, we have a means of doing all those things. We can link any two arbitrary chains together and we have a means of representing these arbitrary state changes. We can provide that continuity as a service." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentive Alignment " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because we're IPLD based, we actually can relatively easily take our archive and push it into Filecoin, where there can then be this clear monetization strategy for storing the data. Because we monetize the transmission of the data, which is a much easier problem to solve than the verifiable storage of Filecoin, we're providing an incentive for why someone would do that. Think about it. There are different incentives throughout the process. There's an incentive for including the transaction. That incentive is very clear, but there's not really any incentive in any blockchain I'm aware of for why I should then send that data. Why should I satisfy a read from a user? A user asks for a read, and why do I care?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That's what Laconic is trying to solve–we're incentivizing the reading of that data. And by incentivizing the reading of that data, that's step number two. Now we can talk about the incentives of step number three, which is a long-term persistent storage of that data. Because if you think about just having the incentives of just Filecoin and just Ethereum, you have this gap in the middle. Why do I take the Ethereum data and transform that and publish it to Filecoin? There's not really an incentive for me to do that." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whereas with Laconic, there starts to become more of an incentive to do that because I need to support my own read infrastructure. People will come to you and know to come to a single place to get their historic reads as well as their more recent reads. And so you’ll be incentivized to charge them. There will already be an ecosystem in place where people are accustomed to paying for data. And when they want to pay for old data or new data, they'll come to the same place, buy that data, and that will incentivize archival storage. Right now we don't have a very good model for why archival storage persists. And it is a real mechanism design issue actually." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "InterPlanetary Linked Data (IPLD) is the core of our system. The first thing we do is take the Ethereum data, which could be any blockchain or any hash linked data structure, and we convert that into an IPLD object. We then index it in that context. We’re storing the RLP encoded bytes, but we are also storing the CID (Content ID), the multi-format address of that object. That's how we're able to generate evidence." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On Ethereum, you have transaction receipts and you have the event messages. When you have an event message on Ethereum, the event message does not prove all the way back up to the root. So when you have a set of events, which is what The Graph consumes, the way that you prove that event is correct is that you find the block that that event was in, and then you rerun that whole block and at the end of it you see if you have the same event that you started with. Whereas, if I have an account balance on Ethereum, I have a block number and then I can get a proof. So I don't have to recompute the whole block to figure out the account balance in that block. I just get the proof from the Ethereum client about that account balance at that block, and I can present that proof and the balance to the user using eth_getProof." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But the actual logs in Ethereum are not provable in this way. This is why The Graph isn't provable and there are a lot of consequences from this. But because we use IPLD, we can create those hash links. Where the link was missing in the original Ethereum protocol, we can augment that protocol and generate a proof using the Ethereum data and our additional links, which are relatively easily. It's not some weird, crazy different format. It's this format that is very similar to the existing Ethereum formats, that prove that this log actually came from this block." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Member Validators" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic L2 has seven Founding Members right now. These seven Members validate, ingest the blocks, and make commitments to the state of those blocks. They then share that information with a paying customer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are plans to increase the validator set. From a customer perspective, if our customer is a Dapp developer and they're saying, “right now I have to use Infura, Alchemy, Blocknative to assert that my data is correct because if one of them goes down for whatever reason, that's three right there.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That sounds like a pain in the ass. With Laconic, you integrate one protocol and you get seven Member Validators instead of three, and you get an assertion from us that you can verify yourself that we're actually physically located in different places. Alchemy and Infura both run in AWS, I presume. If AWS goes down, you just lost two out of your three, if not all three out of your three in that case. Seven is a low number, but seven is incredibly high compared to what people have right now, or they think they have four and they have one, whereas we're positively asserting that you have seven." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "RPC Services and Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the path to building Watchers, we realized we had to build extremely performant RPC endpoints, and we had to build out a deployment system. We realized that that was actually what people wanted to buy from us. Most Dapps don’t want to bother with Watchers right now. What they want to see is this immediate savings on the RPC endpoint side. From there, oncet our foot is in the door, we can say, \"Well, we can give you even more savings. You are using that RPC endpoint to build your own indexer. We have a whole library of tools to build indexers that will auto-generate indexers for you. And we have a marketplace where you can go to get other people to run that indexer for you when you don't want to scale it.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Currently, RPC endpoints are subsidized by VCs. Dapp developers are never experiencing the true cost of running an indexing service or running an RPC endpoint. They're not exposed to that in a free market way. There's this actor, this venture capitalist, who is going in and giving away free samples at a massive scale. The challenge for us is in how we compete with that? There's also a challenge in that our customers are depending on this centralization service and don't realize it. " + } + ] + } + ] + } + } + }, + "featured": false, + "id": "64080923", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Rick Dudley Discusses Laconic Network on The Interop", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Rick Dudley Discusses Laconic Network on The Interop" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Rick Dudley Discusses Laconic Network on The Interop" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1675901476-laconic-seb-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1675901476-laconic-seb-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2023-02-09T00:27:23Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "erc20-watcher-demo", + "title": "ERC20 Watcher Demo", + "date": "2023-01-31", + "category": [ + { + "id": "6311820", + "slug": "developers", + "title": "Developers" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1675106863-laconic_clippy_watcher_02.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In " + }, + { + "url": "https://www.laconic.com/blog/intro-to-the-laconic-stack", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the last blog post" + } + ] + }, + { + "type": "span", + "value": ", we introduced all the main components of the Laconic Stack. The first point of entry for any developer wishing to use Laconic is " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "span", + "value": ". It allows you to stand up a local fixturenet for testing purposes. Integrated directly into Stack Orchestrator are several \"stacks\" which work out of the box. Today, we'll be going over the ERC20 \"stack\" to provide an overview of some key components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You will accomplish the following:\n" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "stand up the core stack using Stack Orchestrator" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 token" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 Watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "send tokens to and from your local account to a new account on Metamask" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "use GraphQL to query the watcher for information about the token and accounts" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This tutorial assumes you are on a local machine (Mac or Linux). Trying it in the cloud requires additional configurations (e.g., opening ports) not covered here." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Pre-requisites" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "python3" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://www.python.org/downloads/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/get-docker/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker-compose" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/compose/install/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MetaMask " + }, + { + "url": "https://metamask.io/download/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "span", + "value": " in the supported browser of your choice." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If using a fresh Ubuntu Digital Ocean droplet, check out " + }, + { + "url": "https://github.com/LaconicNetwork/Laconic-Documentation/blob/staging/scripts/install-laconic-stack.sh", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this script" + } + ] + }, + { + "type": "span", + "value": " for a quick setup." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WARNING" + }, + { + "type": "span", + "value": ": if installing docker-compose via package manager (as opposed to Docker Desktop), you must install the plugin, e.g., on Linux:" + } + ] + }, + { + "code": "mkdir -p ~/.docker/cli-plugins\ncurl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose\nchmod +x ~/.docker/cli-plugins/docker-compose", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, install the latest release of Stack Orchestrator" + } + ] + }, + { + "code": "curl -L -o laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Give it permission:" + } + ] + }, + { + "code": "chmod +x laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Verify operation:" + } + ] + }, + { + "code": "./laconic-so \nUsage: python -m laconic-so [OPTIONS] COMMAND [ARGS]...\n\n Laconic Stack Orchestrator\n\nOptions:\n --stack TEXT specify a stack to build/deploy\n --quiet\n --verbose\n --dry-run\n --local-stack\n --debug\n --continue-on-error\n -h, --help Show this message and exit.\n\nCommands:\n build-containers build the set of containers required for a complete...\n build-npms build the set of npm packages required for a...\n deploy-system deploy a stack\n setup-repositories git clone the set of repositories required to build...\n version print tool version", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For a more permanent setup, move the binary to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "~/bin" + }, + { + "type": "span", + "value": "` and add it your `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PATH" + }, + { + "type": "span", + "value": "`." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so" + }, + { + "type": "span", + "value": "` CLI tool makes it easy to experiment with various components of the stack. It allows you to quickly and seamlessly experiment with watchers. Because it uses docker/docker-compose, several commands in this tutorial will leverage the ability to execute commands directly in the containers. This, for example, means that `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "yarn" + }, + { + "type": "span", + "value": "` doesn't need to be installed on your local machine." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Setup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Use the stack orchestrator to pull the core repositories:" + } + ] + }, + { + "code": "./laconic-so --stack erc20 setup-repositories", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see something like:" + } + ] + }, + { + "code": "Dev Root is: /root/cerc\nChecking: /root/cerc/go-ethereum: Needs to be fetched\n100%|####################################################################################################| 71.6k/71.6k [00:23<00:00, 3.10kB/s]\nChecking: /root/cerc/ipld-eth-db: Needs to be fetched\n100%|##########################################################################################################| 595/595 [00:00<00:00, 991B/s]\nChecking: /root/cerc/ipld-eth-server: Needs to be fetched\n100%|####################################################################################################| 25.5k/25.5k [00:06<00:00, 3.82kB/s]\nChecking: /root/cerc/watcher-ts: Needs to be fetched\n100%|####################################################################################################| 8.79k/8.79k [00:01<00:00, 4.49kB/s]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we'll build the docker images for each repo we just fetched." + } + ] + }, + { + "code": "./laconic-so --stack erc20 build-containers ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This process will take 10-15 minutes, go make a pot of coffee. The output will give you an idea of what's going on. Eventually, you'll see:" + } + ] + }, + { + "code": "Successfully built 77c75d57ad66\nSuccessfully tagged cerc/watcher-erc20:local", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, let's deploy this stack:" + } + ] + }, + { + "code": "./laconic-so --stack erc20deploy-system up", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The output will looks like this (ignore the warnings):" + } + ] + }, + { + "code": "WARN[0000] The \"eth_proxy_on_error\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_forward_eth_calls\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_http_path\" variable is not set. Defaulting to a blank string. \n[+] Running 23/23\n ⠿ ipld-eth-db Pulled 18.4s\n ⠿ 213ec9aee27d Already exists 0.0s\n ⠿ 85c3ef7cf9a6 Pull complete 0.7s\n ⠿ ac29cc04759a Pull complete 0.9s\n ⠿ 2a37e244d86b Pull complete 13.5s\n ⠿ 36d7202aa1cf Pull complete 13.8s\n ⠿ 3acdddb9790a Pull complete 13.9s\n ⠿ 9a938759f2bf Pull complete 14.1s\n ⠿ 5d65a6241248 Pull complete 14.2s\n ⠿ cee6999f074e Pull complete 14.4s\n ⠿ 20b12472cb73 Pull complete 14.8s\n ⠿ 65467bb36f5f Pull complete 16.2s\n ⠿ fe6050bae51d Pull complete 17.4s\n ⠿ 519306d43b4a Pull complete 17.9s\n ⠿ erc20-watcher-db Pulled 15.0s\n ⠿ 8921db27df28 Already exists 0.0s\n ⠿ eb286326f602 Pull complete 0.3s\n ⠿ 63139c77dd7e Pull complete 0.5s\n ⠿ 17baeacd3984 Pull complete 13.5s\n ⠿ 5f08b9782916 Pull complete 13.8s\n ⠿ a836be7ad658 Pull complete 14.0s\n ⠿ 1966853affaf Pull complete 14.2s\n ⠿ 4dc6d2c8dede Pull complete 14.4s\n[+] Running 8/8\n ⠿ Network laconic-30c27a9be20b005274dfc23fd7e90256_default Created 0.1s\n ⠿ Volume \"laconic-30c27a9be20b005274dfc23fd7e90256_erc20_watcher_db_data\" Created 0.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1 Healthy 33.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1 Healthy 34.8s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1 Started 32.7s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1 Started 33.1s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1 Healthy 53.5s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1 Started 54.3s", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's take stock of what just happened, we:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "cloned a bunch of repos: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so setup-repositories" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "built all of their docker images: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so build-containers" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed these images as services that know about each other: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so deploy-system up" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Take a look at all the running docker containers:" + } + ] + }, + { + "code": "docker ps", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You should see 6 containers:" + } + ] + }, + { + "code": "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n605ccf0e4461 cerc/watcher-erc20:local \"docker-entrypoint.s…\" 6 minutes ago Up 5 minutes (unhealthy) 0.0.0.0:3002->3001/tcp, 0.0.0.0:9002->9001/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1\n0a00a3a1bcd6 cerc/ipld-eth-db:local \"/app/startup_script…\" 6 minutes ago Up 5 minutes laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1\nf4aece866e48 cerc/ipld-eth-server:local \"/app/entrypoint.sh\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8081-8082->8081-8082/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1\nebe0dc8cd2b4 cerc/go-ethereum-foundry:local \"./start-private-net…\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8545-8546->8545-8546/tcp laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1\n72263d100b8c postgres:14-alpine \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 0.0.0.0:15433->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1\nd2effc54624c timescale/timescaledb:2.8.1-pg14 \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 127.0.0.1:8077->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, via the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "watcher-erc20" + }, + { + "type": "span", + "value": "` container, the " + }, + { + "url": "https://graphql.org/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "span", + "value": " playground is enabled on " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and you should check that it is there:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great so now we have the core stack up and running, let's deploy an ERC20 token." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, we need the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER ID" + }, + { + "type": "span", + "value": "` of the ERC20 watcher:" + } + ] + }, + { + "code": "docker ps | grep \"watcher-erc20\"", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Using the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ID" + }, + { + "type": "span", + "value": "` from the example above, we'll export the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER_ID" + }, + { + "type": "span", + "value": "` for use throughout the rest of the tutorial:" + } + ] + }, + { + "code": "export CONTAINER_ID=605ccf0e4461", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we can deploy an ERC20 token (currency symbol GLD):" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:deploy:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and your output should look like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-deploy\nDownloading compiler 0.8.0\nCompiled 5 Solidity files successfully\nGLD Token deployed to: 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\nDeployed at block: 9087 0x4dc63b4b2695b644d7d390d70c9de0232399ea4d54b8c75911eee14c13f9ceaf\nDone in 157.39s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, now that we've deployed the GLD token, you'll want to export its address for later use:" + } + ] + }, + { + "code": "export TOKEN_ADDRESS=0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Get your primary account address with:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn account:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and the following output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker account\n0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nDone in 21.63s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "export that address to your shell:" + } + ] + }, + { + "code": "export PRIMARY_ADDRESS=0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To get the latest block hash at any time, run:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "for an output like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker block-latest\nBlock Number: 12783\nBlock Hash: 0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\nDone in 21.44s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll configure MetaMask." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "MetaMask" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Open MetaMask in your browser:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Add Network\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Scroll to the bottow and click \"Add Network Manually\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Put in this information:" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you see the error above \"This URL is currently used by the Localhost 8545 Network\", change `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "localhost" + }, + { + "type": "span", + "value": "` to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "127.0.0.1" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We will come back to MetaMask later and complete this process; for now, copy your new address" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and export it for later:" + } + ] + }, + { + "code": "export RECIPIENT_ADDRESS=0x988a070c97D33a9Dfcc134df5628b77e8B5214ad", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Head on over to " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and paste the following (but with your variables):" + } + ] + }, + { + "code": "query {\n name(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n symbol(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n totalSupply(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "then click \"Run\" and you'll see a response like this:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"name\": {\n \"value\": \"Gold\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzavwb52aq6smf6movgcimvuoggp3cifayb2vyidg3ar546pwtb3dea\\\",\\\"ipldBlock\\\":\\\"0xf843a032575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85ba1a0476f6c6400000000000000000000000000000000000000000000000000000008\\\"}}}]\"\n }\n },\n \"symbol\": {\n \"value\": \"GLD\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzanp5bxcn6wqd2yptbbwo5o4rx3mhpji43yd7sfd42suq6hjuhuroq\\\",\\\"ipldBlock\\\":\\\"0xf843a03a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19ba1a0474c440000000000000000000000000000000000000000000000000000000006\\\"}}}]\"\n }\n },\n \"totalSupply\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzasfla7wzuessejihdtrxqd5lqxc57egukbbricizz2ssrltex4uvq\\\",\\\"ipldBlock\\\":\\\"0xeca0305787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace8a893635c9adc5dea00000\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here's what it'll look like in the browser:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A lot has happened thus far, so let's review; we've:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "downloaded the core repos, built their docker images, and launched a local network (all using stack orchestrator)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed an ERC20 token, added it to our MetaMask account" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "used the GraphQL playground to query the ERC20 watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "exported a handful of shell variables which are about to come in handy" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll use the playground to query account balances:" + } + ] + }, + { + "code": "query {\n fromBalanceOf: balanceOf(\n # latest block hash\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # primary account having all the balance initially\n # created by stack orchestrator\n owner: \"0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\"\n ) {\n value\n proof {\n data\n }\n }\n toBalanceOf: balanceOf(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # address copied from MetaMask, has no balance initially\n owner: \"0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "the primary address should have " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " 1000000000000000000000 and the recipient address should have 0:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzarsopkngjoijjyktfhgckq7te4dsk25gfyj653uxu4kcwqoyuykiq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5dea00000\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"0\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"\\\",\\\"ipldBlock\\\":\\\"0x\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note also that the recipient address also does not yet have a `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "cid" + }, + { + "type": "span", + "value": "` or `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ipldBlock" + }, + { + "type": "span", + "value": "`, which makes sense; this is a random account we just created and hasn't received any transactions. The network does not know about it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send it some GLD!" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:transfer:docker --token $TOKEN_ADDRESS --to $RECIPIENT_ADDRESS --amount 100000000 ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see a familiar output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-transfer --token 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550 --to 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad --amount 100000000\nNothing to compile\nTransfer Event at block: 13371 0x412dbc25599773bfe929c67882e4a001b9d1b3b8e1c60ad4a495d5306608c77a\nfrom: 0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nto: 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\nvalue: 100000000\nDone in 26.12s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now get the latest block hash:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and go back to the GraphQL playground. If you've changed nothing since the last query, update only the latest block hash and run the query again, you'll see the updated account balances:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"999999999999900000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzahg5shtf2rr7pompx7yx6r22zu4lea7ftlnbqkqcabvndsrvoljhq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5d8aa1f00\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"100000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzas6xotntgq4u3v4eui6pmtbyttgikzmu7mppknam2wrekhoynupjq\\\",\\\"ipldBlock\\\":\\\"0xe7a03305adb1a8efab310b21e03d5a9f08d8cf98815372c2c4d8068e1359b8f996bc858405f5e100\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, you've now used a watcher to see query token balances." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send some tokens from the MetaMask recipient account back to the primary account." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Recall that when adding the network to MetaMask, we used the currency symbol \"GLD\". However, this does not mean that MetaMask can auto-detect the token, therefore, we will have to manually import it:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Copy the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TOKEN_ADDRESS" + }, + { + "type": "span", + "value": "` and paste it in the popup. The two other fields should auto-complete:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Import Token\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and now you'll see your balance. Ignore the GLD token from earlier." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, send some tokens back to the primary address using MetaMask:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Make the gas price `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "0" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Grab the latest block hash (again) and fire off the GraphQL query for account balances to see the change." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Voila! You've successfully stood up the core Laconic stack, deployed an ERC20 token, and queried account balances." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cleanup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tear down your docker containers with:" + } + ] + }, + { + "code": "./laconic-so deploy-system --stack erc20 down", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Next steps" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Try out the " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator/tree/main/app/data/stacks/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "ERC721 demo" + } + ] + } + ] + } + ] + } + } + }, + "featured": false, + "id": "64023526", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "ERC20 Watcher Demo", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "ERC20 Watcher Demo" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "ERC20 Watcher Demo" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1675106863-laconic_clippy_watcher_02.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1675106863-laconic_clippy_watcher_02.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2023-01-31T18:52:55Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "intro-to-the-laconic-stack", + "title": "Intro to the Laconic Stack", + "date": "2023-01-18", + "category": [ + { + "id": "6311820", + "slug": "developers", + "title": "Developers" + } + ], + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1673986992-laconic_clippy_grid2.png" + }, + "content": { + "blocks": [ + { + "id": "63992474", + "title": "Laconic Stack Diagram" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here’s the main problem: reading data from the Ethereum blockchains is either cheap and sloppy or expensive and correct. As a result, Dapp developers have come to rely on inexpensive centralized services that do not provide evidence to verify the correctness of the data they are serving to Dapps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only is it expensive to get verifiable data but it can also be challenging to parse out the subset of data you really need. In the early days of SQL, you had to be proficient at the command line in order to use the product, and so use was limited to those that had that specialized capability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Eventually, GUIs were built such that anyone with basic computer skills could use drop-down menus and create database schemas without writing a single line of code. Web3 is still in the early days of SQL, it is not easy onboarding new users and developers who are otherwise quite capable with the latest Web2 technologies.\n\nRight now, there’s all this data on Ethereum and as a Dapp developer, you only want a tiny fraction of it. But, to verify that fraction, you have to (among several other things) maintain an archive node - this is prohibitively expensive for the majority of developers. To solve this problem, centralized services such as (Infura, The Graph, and Alchemy) have popped up and currently account for the majority (if not most) of Dapp queries to Ethereum." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic was created to address these and several other problems in the blockchain ecosystem. Not only does Laconic make it easy to get verifiable data - quickly and cheaply - it also provides a framework for data transformation and aggregation that are difficult or impossible to do in other systems.\n\nArchitecting a solution to this requires many moving pieces; these have been developed by core Ethereum & Cosmos contributors over the past 5 years. In this post, we will walk you through the various components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are three different ways to participate in the Laconic Network: Member Validators, Service Providers, and Dapp Developers. To describe the responsibilities and benefits of each role, we must first start grounded in the technicalities of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let’s take a look at the following core stack diagram:" + } + ] + }, + { + "item": "63992474", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note: this diagram intentionally leaves out several repositories (e.g., codecs, utilities, rpc shims). This is done for simplicity reasons and anyone diving deep into the stack will discover them.\n\nThe two repositories at the top are also the main entry points for most developers. `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` is a command-line tool for, well, orchestrating the stack. It uses docker-compose to deploy a specified collection of networked docker containers, thereby eliminating the need to set up a variety of services independently. Every user of the Laconic Stack will at some point - if not regularly - use the stack orchestrator." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watchers-ts" + } + ] + }, + { + "type": "span", + "value": "` repo contains the publically available Watchers and the code to generate them. Watchers are TypeScript that is generated from one or more Solidity smart contracts. Dapp Developers can participate in the Laconic Network by either 1) writing a custom watcher for their Dapp or 2) writing a generally useful watcher and publishing it to the Laconic Registry, thus earning a fee every time it is used. We’ll come back to Watchers later." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Down at the bottom left is the `" + }, + { + "url": "https://github.com/cerc-io/laconicd", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconicd" + } + ] + }, + { + "type": "span", + "value": "` repository and it is indeed the “bottom” of the stack. It is built from the Cosmos SDK and has custom modules specific to operating the Laconic Network (e.g., fork of Ethermint/Evmos, auction, nameservice). It is likely that in the future there will be a public testnet, however, because the Laconic Network is a permissioned validator set, only Member Validators that have officially joined the Laconic Network will be included in the mainnet. Just because the validator set is permissioned does not prohibit anyone from running a full node and Service Providers or others may choose to do so for a variety of reasons." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/laconic-sdk", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-sdk" + } + ] + }, + { + "type": "span", + "value": "` is a library for facilitating talking to `laconicd`. Both the `" + }, + { + "url": "https://github.com/cerc-io/laconic-registry-cli", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-registry-cli" + } + ] + }, + { + "type": "span", + "value": "` and the `" + }, + { + "url": "https://github.com/cerc-io/laconic-console", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-console" + } + ] + }, + { + "type": "span", + "value": "` use it. While `laconic-registry-cli` is a command-line tool for doing so, the `laconic-console` is a user interface for writing and reading records on the Laconic Network. These general-purpose tools are useful for a wide variety of use cases across the stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A key goal of the Laconic Network is to provide accurate, verifiable data from the Ethereum blockchain. Comparisons to currently available solutions are for another post, however, no service currently exists to provide inexpensive evidence that the data being served is correct. The Laconic solution (one of) to this is in something called “statediffing”, a part of the stack run by Member Validators and, likely by Service Providers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It starts with a maintained fork of `" + }, + { + "url": "https://github.com/cerc-io/go-ethereum", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "go-ethereum (geth)" + } + ] + }, + { + "type": "span", + "value": "` that has an added real-time state-diffing service. Statediffing gives a clear picture of the state between any given blockheights. This allows Laconic to minimize the amount of computation required for providing proofs. Three additional “helper” services perform different tasks required to get a full picture of the state as required by an application. Together, these comprise the Full Index Node (FIN)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-service" + } + ] + }, + { + "type": "span", + "value": "` provides historical state data, while the `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-fill-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-fill-service" + } + ] + }, + { + "type": "span", + "value": "` uses the historical state data to fill statediff gaps as required. Finally, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-state-snapshot", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-state-snapshot" + } + ] + }, + { + "type": "span", + "value": "` loads a complete state at a certain blockheight, which helps to bootstrap the system. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nThere is more to be written about statediffing, however, what’s important to note here is that each service is writing independently to `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-db", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-db" + } + ] + }, + { + "type": "span", + "value": "`. The latter serves as a bucket for state data that has been indexed in IPLD. Rather than querying this database directly, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-server", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-server" + } + ] + }, + { + "type": "span", + "value": "` provides an API layer for Watchers to easily query relevant pieces of data from the Ethereum state. Additionally, `ipld-eth-server` recapitulates the native Ethereum JSON RPC interfaces on top of the `ipld-eth-db` database." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "And so we’ve come full circle back to the Watchers. As previously mentioned, they are generated from one or more Solidity smart contracts and configured to query specific pieces of data relevant to a Dapp. Watchers make it easy to query the data you need from Ethereum " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "and" + }, + { + "type": "span", + "value": " - along the way - get evidence in order to generate proofs that your data is correct. This is in contrast to currently available solutions for Dapp developers, who must currently rely on centralized providers that don’t provide evidence to generate proofs." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Web3 is (still!) in its early days, and like the early days of SQL, Dapp developers need advanced knowledge of complex data structures to build their Dapp. Watchers simplify this by exposing a GraphQL endpoint, a solution familiar to an order of magnitude more developers than querying the Ethereum blockchain directly." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is building a suite of tools to address core problems in Web3. Today, we’ve provided an overview of the main components of the Laconic Stack. Developers interested in Laconic should start with `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` to get a sense of running different parts of the stack, then check out `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watcher-ts" + } + ] + }, + { + "type": "span", + "value": "` to experiment with different watchers and progress to making their own." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "63992475", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Intro to the Laconic Stack", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Intro to the Laconic Stack" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Intro to the Laconic Stack" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1673986992-laconic_clippy_grid2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1673986992-laconic_clippy_grid2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2023-01-17T20:35:35Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "trends", + "title": "A Look Ahead at Crypto in 2023", + "date": "2023-01-05", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1672273151-screen-shot-2022-12-28-at-4-19-05-pm.png" + }, + "content": { + "blocks": [ + { + "id": "63885761", + "title": "Prop 82" + }, + { + "id": "63885758", + "title": "Akash Validator Set" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So much has changed in the last year due in large part to a seemingly unpredictable sequence of epic market failures. Despite this, we’re still here trying to predict the unpredictable. As the dust settles from a cascade of collapses including FTX, Terra, 3AC, and many others, the direction crypto must take to become viable is now clearer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We find ourselves in a strange position. We built protocols that actually work under immense market pressure, showing some of the promise of decentralized systems. However, that pressure came from deeply irresponsible market manipulation and behavior. How do we make sense of the fallout, and do better. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Blockchain Regulation: it’s coming" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, this space is about to get regulated. The " + }, + { + "url": "https://www.pillsburylaw.com/en/news-and-insights/digital-commodities-consumer-protection-act-digital-assets.html", + "type": "link", + "children": [ + { + "type": "span", + "value": "Digital Commodities Consumer Protection Act" + } + ] + }, + { + "type": "span", + "value": " (DCCPA) looms in the wake of FTX’s demise and Sam Bankman Fried’s increasingly radioactive support of the bill. The DCCPA aims to give the CFTC primary jurisdiction over crypto exchanges. This would establish the commission as the authority on spot, margined and leveraged digital commodity trades. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The bill, however, does not outline processes for classifying assets as commodities and securities. This is relevant because the Securities and Exchange Commission (SEC), which under Gary Gensler has increasingly strived for more oversight, currently qualifies most crypto, aside from Bitcoin, as a security. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In Europe the new Markets in Crypto Assets Regulation (MiCA) aims to regulate dollar-pegged stablecoins, controlling transaction count and volume limits which, " + }, + { + "url": "https://www.reuters.com/technology/eu-crypto-rules-set-cap-dollar-pegged-stablecoins-2022-10-07/#:~:text=LONDON%2C%20Oct%207%20(Reuters),competitiveness%2C%20industry%20representatives%20have%20said.", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "according to Reuters" + } + ] + }, + { + "type": "span", + "value": ", are already exceeded by the three largest stablecoins, Binance USD, USD Coin & Tether. Stablecoin volume has increased dramatically over the last several years and a cap or ban could hinder the crypto ecosystem overall. Crypto regulation and the conversations around asset classifications, regulatory purview, and stablecoin usage will be defining battlegrounds in 2023. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Decentralization: yes it matters" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Increasing regulatory pressure will force crypto projects to re-focus on achieving actual decentralization; not just to avoid legal consequences but also to empower more equitable and effective on-chain governance. Although there is no official guidance from the SEC, commissioner " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Hester Pierce’s Safe Harbor Proposa" + }, + { + "type": "span", + "value": "l has served as a guiding light for those interested. The proposal suggests networks must achieve sufficient decentralization within three years, meaning networks should not be controlled by a single individual or group of individuals. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tokens must also be used to exchange value pertaining to the network. The token should be sold for facilitating access to, participation on, or development of the network. Essentially, if it’s a decentralized network, validators/investors/teams cannot control majority stake. If the network has a token, then the token needs to be used for something " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "actually" + }, + { + "type": "span", + "value": " intrinsic to network operations–it can’t just be a fundraising vehicle." + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "On-Chain Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve touched on a number of external forces impacting networks but what about internal forces? On-chain governance is rapidly evolving. Usage of proof of stake (PoS) consensus has grown over the last several years in part due to a dramatic reduction in power consumption, and also because of increased flexibility to affect significant changes to network parameters. With voting power relative to stake, stake concentrated in the hands of a few entities can present unique and potentially insurmountable challenges to achieving sufficient decentralization." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Cosmos ecosystem is a particularly fascinating testing ground for on-chain governance. From the controversial " + }, + { + "url": "https://www.mintscan.io/juno/proposals/16", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Juno Prop 16" + } + ] + }, + { + "type": "span", + "value": " which resulted in on-chain confiscation of user funds, to the equally controversial " + }, + { + "url": "https://www.mintscan.io/cosmos/proposals/82", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Cosmos Prop 82" + } + ] + }, + { + "type": "span", + "value": " which put forward an entirely new paradigm for Cosmos Hub utility and ATOM value capture, the Cosmos ecosystem has been trailblazing governance processes and implementation. " + } + ] + }, + { + "item": "63885761", + "type": "block" + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What’s at Stake?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the main issues we’re seeing now is the disproportionate concentration of stake. In Cosmos for instance, the top 5 out of 150 " + }, + { + "url": "https://www.mintscan.io/juno/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Juno validators" + } + ] + }, + { + "type": "span", + "value": " control 25% of the network. The top 5 out of 175 " + }, + { + "url": "https://www.mintscan.io/cosmos/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Cosmos validators" + } + ] + }, + { + "type": "span", + "value": " control 25% as well. The top 5 out of 100 " + }, + { + "url": "https://www.mintscan.io/akash/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Akash validators" + } + ] + }, + { + "type": "span", + "value": " control 37%. " + } + ] + }, + { + "item": "63885758", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In Ethereum, " + }, + { + "url": "https://cointelegraph.com/news/64-of-staked-eth-controlled-by-five-entities-nansen", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "according to Nansen" + } + ] + }, + { + "type": "span", + "value": ", there are 5 entities that control 64% of staked Ether. Although only 11% of circulating ETH is staked, among 426,000 validators, three major cryptocurrency exchanges account for 30% of the total staked ETH, with Lido DAO accounting for 31%. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If the top 4-5 Validators control 1/4 of the stake and, at times more, of a network, is the network really decentralized? If an exchange like Binance or Huobi can buy up large volumes of tokens, enter the top validator slots, and through governance, make substantive changes to the network for their own benefit, is that network truly decentralized? There are valid arguments on both sides. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There’s an argument that these networks are permissionless and any entity can engage in governance by purchasing tokens and staking them, so in theory, they are decentralized. In practice, power is often concentrated in the hands of a few entities with a majority of the tokens. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The current Cosmos governance landscape is also fascinating because many pivotal governance mechanisms and network changes are being deliberated simultaneously. Should there be a constitution? Should there be larger deposit requirements for proposals? Should ATOM issuance be changed to fund network growth initiatives–completely repurposing the network in the process? Cosmos will continue to innovate on-chain governance. As Cosmos gains more visibility, developers will adapt components of Cosmos governance for their purposes. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Novel Solutions to Crypto’s Most Existential Problems" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As we look ahead to 2023, Laconic is well positioned to address these issues. Laconic Network leverages Cosmos technology and is run by an LLC that is designed to comply with the laws within its jurisdiction and to allow each member to comply with the laws within their jurisdiction. Geographic distribution of member validators strengthens the network's resilience to nation specific sanctions. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Additionally each member validator is given one vote and all votes are equally weighted. This eliminates uneven distribution of voting power typically seen in delegated proof of stake networks. Laconic Network also makes it clear that Laconic Network Token (LNT) is treated as a loyalty point and for prepayment of services, and is not a security or currency. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For more on the Laconic Network Governance model and how it addresses the fundamental issues around asset classification, token usage, PoS networks and regulation, you can check out “" + }, + { + "url": "https://www.laconic.com/blog/laconic-governance-model", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "A New Governance Model for the New Web" + } + ] + }, + { + "type": "span", + "value": ".” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Crypto isn’t going anywhere, but we will need to build safer and more decentralized systems, and work within a more aggressive regulatory environment. " + } + ] + } + ] + } + } + }, + "featured": false, + "id": "63853186", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "A Look Ahead at Crypto in 2023", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "A Look Ahead at Crypto in 2023" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "A Look Ahead at Crypto in 2023" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1672273151-screen-shot-2022-12-28-at-4-19-05-pm.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1672273151-screen-shot-2022-12-28-at-4-19-05-pm.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2023-01-04T22:13:31Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "laconic-governance-model", + "title": "A New Governance Model for the New Web", + "date": "2022-10-25", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1664245622-governance-blog-4.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network was founded to develop crucial infrastructure for current and future generations of blockchains and Web3 applications. That's an ambitious mission, and one that demands significant technological innovation. Perhaps even more innovative, however, is the company's governance model—a unique organizational " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "structure that, along with the Laconic token, underlies" + }, + { + "type": "span", + "value": " a scalable and sustainable project with the power to address the many operational, regulatory, and ideological challenges of Web3. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Operational challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network’s governance model addresses four primary operational challenges:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Network funding. The Laconic governance model funds initial network development and growth. But unlike most venture capital-funded businesses, Laconic avoids passive investment, instead requiring all founding members to make significant contributions of capital, engineering expertise, or both." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Network growth. To ensure timely continued growth, the Laconic Network must scale along with demand for Web3 data services—necessitating clear procedures for adding and removing Members and Service Providers." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Hardware operations. Laconic indexing and services are computationally expensive, requiring significant hardware and infrastructure investment. Laconic is creating a decentralized infrastructure network built for scalability and flexibility, and designed to deliver data to consumers at the highest possible service level." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Corporate governance. Clear and fair governance is essential to any decentralized project. The Laconic limited liability company agreement (LLCA) clearly specifies how stakeholders including Members, Service Providers, and data customers may participate in the governance process; proposal submission and voting processes; and proposal requirements for specific changes and updates." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Regulatory challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The governance model addresses three primary regulatory challenges: " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Making it clear that the Laconic LNT token is solely a loyalty point or prepayment for services—not a security or currency" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ensuring that on-chain private auction of membership interests in a Cayman Island LLC is fully compliant with US securities law" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Protecting minority rights for all shareholders of Laconic LLC" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Ideological challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As Web3 scales, Laconic Network aims to become an essential layer in service of verified blockchain data to applications. The company's governance model must be designed to:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Reward active Network participants through incentives for actions that help build and run the network, including becoming Members or Service Providers, writing Watchers, and consuming network data" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Discourage attempts to speculate on, or otherwise passively profit from, the network" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Corporate structure" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic LLC structure lays out a flexible but binding legal framework for the company's relationship with Network Members. It also provides for treasury management and related off-chain governance solutions while the network is being built:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network, as a corporate entity, is a Cayman Islands LLC made up of members who are themselves corporations. Members work together to build and operate the Network, for which they are required to act as Service Providers and Validators; as a Validator, each member controls a share of the Laconic Network Liquidity Pool. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The corporation's purpose is to sell data in a way that provides purchasers with cryptographic evidence, which ensures reimbursement if a Service Provider fails to meet a quality of service guarantee. Each membership interest in Laconic LLC is indivisible and nontransferable, and represents an actual security per the S.E.C. definition of the term." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On-chain membership interests are determined based on a combination of each Member's capital accounts and individual Liquidity Pool shares—a structure designed to reward members for acting as Validators and Service Providers while promoting healthy competition. The Members’ treasury collateralizes the Network, protecting Service Providers and users." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network currently has seven Founding Members, who brought the project into existence by funding its treasury and providing the engineering work to create the Laconic Stack and Laconic App. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Founding Members have been selected to ensure enough initial Validators and Service Providers to run the hardware needed to index and serve data to end users." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Requiring that Members both fund the Network and provide technical services ensures that all Members are motivated to make the project successful. There is no passive investment—for example, from VCs who buy the token at a discounted price to fund the Network, then wait for a pump to sell. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Selecting seven Founding Members supports accountability for a minimum viable level of decentralization. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members operate within multiple legal jurisdictions, preventing the Laconic Stack, Validators, and Service Providers from concentrating in too few data centers or cloud providers." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As demand for the services provided by Laconic Network increases, it will be necessary to add Members in order to increase the capacity and diversity of available Service Providers. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As Members are added, Membership totals must always take the form of 6n+1—ensuring that simple majority votes will never be evenly split, and that Validators will never be paid for Byzantine fault tolerance (BFT) that they do not provide. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "New Members are added via auction. To participate in an auction, potential Members must prove that they are technically qualified and have the funds needed to buy a Membership Interest from one or more existing members." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To demonstrate their ability to operate the Laconic Stack at a level of quality comparable to that of existing Members, potential Members must participate in a testnet." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Potential Members who successfully complete the testnet are invited to a private auction of a lot of six new Membership Interests. Auction proceeds are added to the Liquidity Pool, minus a Seller’s Reward for existing Members. Seller’s Rewards, akin to mini liquidity events, provide additional financial motivation for membership. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "At some point in the future, Laconic LLC may choose to change the rules regarding membership and Validator participation." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As required by law, provisions exist by which Members may be voluntarily or involuntarily removed from the Network in order to maintain minority shareholder rights of the membership as a whole." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members wishing to exit may sell their interest back to Laconic LLC, which will then auction it to a new potential Member. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members failing to fulfill Validator or Service Provider duties may be evicted by a governance resolution. " + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Laconic Network Token" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Much of the mechanism design of the Laconic Network is encapsulated in the intended uses of the Laconic Network Token (LNT)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": " LNT provides insurance to the Network's Service Providers and data consumers. Any prepayment for services, or any surplus services that data consumers have paid for but not used, may be redeemed for an asset of value. For Service Providers, LNT simplifies business operations, serving as a single asset representing claims against both a stablecoin and the native currency of the L1 on which they are required to operate. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The LNT also provides guarantees to Network participants, in much the same way that initiating a retail transaction triggers an automatic temporary hold on the buyer's payment card, which is released when the purchase is completed. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note that  LNT’s purpose is to allow for refundable prepayment of services among a federation of Service Providers operating across multiple jurisdictions; the token does not exist in any other capacity. It is not a security, as it does not represent a financial interest in Laconic LLC; it can be redeemed only via authorized transfer within the Liquidity Pool, which is controlled by the Laconic Members. Membership Interests are, however, securities, as they represent both the financial interests and the governance stakes of the Members in Laconic LLC; one Membership represents one vote." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The LNT is considered a voucher for data retrieval, not a general-purpose currency. LNT is procured through the Liquidity Pool by trading 3CRV or ETH, and used to pay Service Providers for data transfers. LNT is also staked by Service Providers, and enables them to register on-chain as such. A reclamation function ensures that the full fixed supply of the token will remain constant and in use at all times, making it unsuitable for long-term holding or speculative investment. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Corporate governance for Laconic LLC will be performed on chain, by voting parties who are both Members and Validators. Voting parties may vote on two types of resolutions:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Ordinary resolutions" + }, + { + "type": "span", + "value": " govern operational details of the network and require a ⅔ vote to pass." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Special resolutions" + }, + { + "type": "span", + "value": " are required to amend the governance articles or shut down the Network. Special resolutions must be passed by a unanimous vote." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Proposals may be submitted by any user of the Network who has more than the equivalent of $1000 USD escrowed as LNT." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "New possibilities for organizational governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s primary goal is to make verifiable blockchain data available at scale to Web3 applications. The company also aims to embody the ideological goals of true decentralization and fair rewards for parties actively participating in the network as Members, Validators, Service Providers, or Watcher writers. The Founding Members have also thought extensively about the utility of the LNT token (as a voucher for data), its role in governance (none at all), and how to grow the network through Validator/Member auctions. Together, these choices yield a novel corporate governance model worthy of study by any organization with similar goals and challenges." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "55641249", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "A New Governance Model for the New Web", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "A New Governance Model for the New Web" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "A New Governance Model for the New Web" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1664245622-governance-blog-4.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1664245622-governance-blog-4.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-12-15T16:07:29Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "monthly-update-dec-2022", + "title": "Laconic Monthly Update: December 2022", + "date": "2022-12-01", + "category": [ + { + "id": "2965426", + "slug": "news", + "title": "News" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1669870811-laconicupdate-blog.png" + }, + "content": { + "blocks": [ + { + "id": "63259974", + "title": "metamask" + }, + { + "id": "63259975", + "title": "testnet" + }, + { + "id": "63259976", + "title": "lp" + }, + { + "id": "63259977", + "title": "public marketing launch" + }, + { + "id": "63260091", + "title": "nft" + }, + { + "id": "63259978", + "title": "events" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Hello Laconians," + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Welcome to our first Laconic Monthly Update! " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth development, we were excited to go live with our public marketing launch in the last quarter." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the three months since, Laconic has grown quickly, and we’ve made huge progress with both product and growth. Despite the arrival of “crypto winter,” our team remains focused on building a platform with the power to ensure that the next generation of blockchain applications are safer, faster, and more useful." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thank you for joining us on our journey!" + } + ] + }, + { + "item": "63259974", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "METAMASK PARTNERSHIP" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This month, we announced that Laconic is partnering with the world’s leading self-custodial wallet, ConsenSys’s MetaMask, and MetaMask founder Dan Finlay, to launch MobyMask, an anti-phishing tool. Laconic's MobyMask-caching Ethereum light client greatly reduces the cost of hosting a trustworthy copy of the MobyMask anti-phishing registry, making the tool accessible and affordable for a wide range of users." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Together, we’re laying the groundwork for an ambitious shared vision: a system with the ability to verify credible sources for a wide range of information types. You can " + }, + { + "url": "https://www.laconic.com/blog/laconic-and-consensys-metamask-launch-mobymask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "read our blog to learn more about the partnership with MetaMask" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259975", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TESTNET" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve successfully launched a testnet for internal development and for testing with Laconic’s Founding Members (Founding Members to be announced). The genesis transaction occurred on August 9, 2022, with all Members in consensus.\n\nWe’re now creating tooling to monitor block production, number of transactions, and Validator health; further tooling will include a dashboard and a block explorer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A public, incentivized testnet is planned for 2023. You can sign up for early access " + }, + { + "url": "https://www.laconic.com/partners#testnet", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "here" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259976", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "LIQUIDITY POOL & PAYMENT CHANNELS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve continued work on the mechanics and logistics of the liquidity pool, state/payment channels that will govern the flow of payments and tokens between our own chain and Ethereum, and auction logic implementation. We’ll also be building out the back end of the Laconic App and its network underpinnings. The next step is to deploy the smart contract for the liquidity pool directly to testnet. " + }, + { + "url": "https://www.laconic.com/blog/introducing-laconic-network", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Learn more about our platform and network" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259977", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PUBLIC MARKETING LAUNCH" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth, we completed the design and development of the Laconic website and community channels, with a successful public launch on August 16. Since then, we’ve scaled online engagement with potential partners, developers, press, and people around the world looking to learn more about Laconic. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "item": "63260091", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WEEKLY GROWTH CAMPAIGNS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve launched 14 campaigns since the website launch, and will continue to scale marketing and community efforts in 2023." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "July 26:" + }, + { + "url": "https://www.laconic.com/blog/introducing-laconic-network", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Introducing Laconic Network" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "July 26:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-different", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Network is Different" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "August 6:" + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watchers: Ensuring Trustlessness in Web3" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "August 30:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Network Uses IPLD" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 6:" + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watchers: Ensuring Trustlessness in Web3" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 13:" + }, + { + "url": "https://www.laconic.com/blog/rick-dudley-on-interchain-fm", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Rick Dudley Discusses Laconic Network on Interchain.fm" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 20:" + }, + { + "url": "https://www.laconic.com/blog/what-is-a-proof", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "What Is a Proof and Why Do You Need One?" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 28:" + }, + { + "url": "https://www.laconic.com/blog/99-problems-but-nfts-aint-one", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "99 Problems But NFTs Ain’t One" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 5:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-improves-the-nft-experience", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Radically Improves the NFT Experience" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 20: " + }, + { + "url": "https://www.laconic.com/blog/laconic-devcon-vi-recap", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic’s Devcon VI Recap" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 25: " + }, + { + "url": "https://www.laconic.com/blog/laconic-governance-model", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "A New Governance Model for the New Web" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "November 8: " + }, + { + "url": "https://www.laconic.com/blog/laconic-and-consensys-metamask-launch-mobymask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic and ConsenSys’s MetaMask Launch MobyMask" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "November 22: " + }, + { + "url": "https://www.laconic.com/blog/why-decentralization-matters", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Why Decentralization Matters" + } + ] + } + ] + } + ] + } + ] + }, + { + "item": "63259978", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "EVENTS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Nothing beats real-life engagement! Conferences and events are key channels for growing audience awareness. Our team canvassed select Ethereum and blockchain conferences with a focus on brand positioning, partnerships, and customer development:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ETHAmsterdam and Devconnect (4/18–25): " + }, + { + "type": "span", + "value": "Going into the launch of our public marketing channels, our team produced " + }, + { + "url": "https://twitter.com/laconicnetwork/status/1521957074841706497", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "a networking event" + } + ] + }, + { + "type": "span", + "value": " to connect with Member teams, investors, and developers attending ETHAmsterdam and Devconnect, a conference dedicated to Ethereum L2 Scaling." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ETH CC Paris (7/18–22): " + }, + { + "type": "span", + "value": "Multiple Laconic Member teams converged at the largest Ethereum conference. This was a great opportunity for us to deepen awareness of Laconic among investors and validators in the Ethereum community. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cosmoverse Medellin (9/27–28): " + }, + { + "type": "span", + "value": "Cosmos’s largest conference was an opportunity for us to build awareness and engagement in the Cosmos ecosystem." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Devcon Bogota (10/7–16): " + }, + { + "type": "span", + "value": "The largest Ethereum developer conference offered the perfect opportunity to drive awareness of Laconic with Ethereum developers. Check out the " + }, + { + "url": "https://www.laconic.com/blog/laconic-devcon-vi-recap", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "recap blog" + } + ] + }, + { + "type": "span", + "value": " to learn what the hot topics were at Devcon." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "SF Blockchain Week and ETH SF (11/1–5): " + }, + { + "type": "span", + "value": "These two Bay Area conferences enabled our team to deepen our network in one of the world’s premier tech hubs." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\n\n" + } + ] + } + ] + } + } + }, + "featured": true, + "id": "63259979", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Laconic Monthly Update: December 2022", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Laconic Monthly Update: December 2022" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Laconic Monthly Update: December 2022" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1669870811-laconicupdate-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1669870811-laconicupdate-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-12-01T17:51:49Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "why-decentralization-matters", + "title": "Why Decentralization Matters ", + "date": "2022-11-22", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1669073122-central_decentral_009.png" + }, + "content": { + "blocks": [ + { + "id": "61461493", + "title": "Blockchain Trilemma" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The emergence of any technology brings with it new language, and changes or expands the possible meanings of existing terms. Blockchain, of course, is no exception—and almost any discussion of it will include lots of talk about decentralization. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We should probably start with a definition. In the context of blockchain, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralization" + }, + { + "type": "span", + "value": " denotes the transfer of ownership and governance to the many from the few. Think of the differences between Prince, a solo songwriter and performer who insisted on absolute and final control over his artistic output, and the Beatles, a band that (despite their well-known internal power struggles) had no designated “lead” singer or sole songwriter. While decentralization may not require absolute accord, it does locate power across a system rather than in one designated point. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s worth noting that many in the Web3 world use the terms " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralized " + }, + { + "type": "span", + "value": "and " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distributed" + }, + { + "type": "span", + "value": " interchangeably" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": ", " + }, + { + "type": "span", + "value": "but this is technically incorrect. In the context of the blockchain, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distributed" + }, + { + "type": "span", + "value": " refers not to ownership or governance, but only to the distribution of parties (in most cases, servers) across a physical or digital space.\n\nIn this context, decentralization isn't a binary value. Instead, it describes the values and characteristics of a system that deliberately locates significant aspects of power across a defined system, rather than at a single point within it. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "But, like, what does decentralization " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "do?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is one of the primary innovations that powers blockchains. As the number of parties participating in the consensus mechanism of a blockchain increases, so does its level of decentralization. Participants' consensus about a given digital truth replaces the need for individual trust agreements, while establishing credible censorship resistance for anyone who wants to " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "do something" + }, + { + "type": "span", + "value": " on that blockchain. Any transaction is agreed upon by all, which allows any two parties to transact without first establishing a specific shared trust. This removes the need for oversight by a third party—for example, a \"financial panopticon\" established to prevent fraud—and reduces associated privacy and security risk. " + } + ] + }, + { + "item": "61461493", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization plays a key role in the Blockchain Trilemma—the need to balance decentralization, security, and scalability. A blockchain's level of decentralization is directly proportional to its ability to withstand attacks from inside or outside the network, and directly correlated with its level of neutrality, with greater decentralization linked to stronger censorship resistance and spam-resistant pricing. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most exciting things about decentralization is that the digital truth agreed upon via consensus may be proven by anyone running a node. Network participants must uphold the established consensus when making new blocks, or their stake will be slashed—they'll lose a significant portion of the benefits of entry. And while block producers are referred to as " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "validators" + }, + { + "type": "span", + "value": ", they perform no defined validation action. Instead, they have a financial incentive to act upon the truth determined by a decentralized quorum of participants." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As the number of participants in a blockchain increases, so does the power of decentralization, preventing participants from stealing someone else’s assets by manipulating transactions submitted for block inclusion. Each member controls the private key for a specific address, so no one can censor or manipulate a transaction once it’s been submitted. In the broadest sense, this system operates counter to centralized payment protocols such as PayPal, which censors transactions and even " + }, + { + "url": "https://www.forbes.com/sites/emilymason/2022/10/27/after-paypal-revokes-controversial-misinformation-policy-major-concerns-remain-over-2500-fine/?sh=2e5a914c30c4", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "levies fines on users for not obeying a set of internally derived rules." + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Until recently, says Laconic cofounder and chief protocol designer Rick Dudley, “we’ve mostly been concerned with usurious intermediaries. So maybe it’s best to think about " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "disintermediation" + }, + { + "type": "span", + "value": " as a primary goal within a decentralized system.\" Profit-driven intermediation, he notes, \"is also far more difficult to add to a well-decentralized system, which resists usury by nature.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Look, I don’t care about jpegs or digital Monopoly money. What’s in it for me?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization doesn't just increase the security of financial assets ... or ape GIFs No one party controls the decentralized network, so " + }, + { + "url": "https://www.coindesk.com/business/2022/06/29/coinbase-is-reportedly-selling-geo-location-data-to-ice/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "your pedigree information can't be given or sold to corporations, governments, or any other third parties without your consent" + } + ] + }, + { + "type": "span", + "value": ", as is dismayingly common on Web2 platforms. Decentralization can also protect network members from arbitrary moralities of a market subset—think the ejection of sex workers from Craigslist, protesters losing their financial platforms under political pressure, or citizens prevented from transacting by their government." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Anything else?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization also maximizes data reliability. A single point of failure can affect huge swaths of the public; imagine the effects of destroying the only server farm servicing a specific bank. Of course, to mitigate the risks associated with power outages, simple server failures, or even terrorist attacks, banks geographically " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distribute" + }, + { + "type": "span", + "value": " their servers, with multiple parties storing multiple copies of data in multiple places across a " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralized" + }, + { + "type": "span", + "value": " network. " + }, + { + "url": "https://en.wikipedia.org/wiki/Byzantine_fault", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Even if a large portion of its participants were to go dark, the network would still function." + }, + { + "type": "span", + "value": "\n\n" + } + ] + }, + { + "type": "span", + "value": "In this scenario, the bank is protected—though you, as a user of the bank, are vulnerable to having your transactions watched, censored, or thrown out altogether. The contents of your account can also be " + }, + { + "url": "https://ij.org/report/policing-for-profit-2/grading-state-federal-civil-forfeiture-laws/irs-cleans-out-bank-accounts/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "seized for potentially arbitrary or extralegal reasons" + } + ] + }, + { + "type": "span", + "value": ". As the recent " + }, + { + "url": "https://cointelegraph.com/news/bitcoin-sinks-to-new-yearly-low-at-16-8k-as-ftx-insolvency-fears-turn-into-contagion", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "FTX meltdown" + } + ] + }, + { + "type": "span", + "value": " made all too clear, centralized cryptocurrency exchanges are even more precarious—and because of their inherent lack of regulation, " + }, + { + "url": "https://www.forbes.com/sites/haileylennon/2022/08/01/bankrupt-crypto-lender-celsius-could-leave-customers-last-in-line-to-get-paid/?sh=4c4005ea5fde", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "prone to leave retail investors holding the bag" + } + ] + }, + { + "type": "span", + "value": ". A more decentralized system could offer individual users far more protection against institutional controls, not to mention far less potential for reckless mismanagement by those holding the keys." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Still a long way to go" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralizing a protocol or network still poses many challenges. Foremost among them is the fact that " + }, + { + "url": "https://www.weforum.org/agenda/2016/05/joseph-stiglitz-are-markets-efficient-or-do-they-tend-towards-monopoly-the-verdict-is-in/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "markets tend to direct capital to the few and not the many" + } + ] + }, + { + "type": "span", + "value": ". Our current way of life is undergirded by a system that counters decentralization at every level, moving power away from the majority. Accepting decentralized systems in the physical world would require changing much of how we think about how society is arranged, and about " + }, + { + "url": "https://www.latimes.com/opinion/op-ed/la-oe-pascrell-live-nation-concert-ticketing-20180517-story.html", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "rapidly shrinking" + } + ] + }, + { + "type": "span", + "value": " access to competition under late capitalism." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Another key point: Governments that cannot monitor private transactions often attempt to sanction users of protocols that protect the anonymity afforded by robust, decentralized censorship resistance. Such moves can be used to" + }, + { + "url": "https://home.treasury.gov/policy-issues/financial-sanctions/sanctions-programs-and-country-information/iran-sanctions", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "uphold international sanctions" + } + ] + }, + { + "type": "span", + "value": "—or " + }, + { + "url": "https://www.wsj.com/articles/crypto-advocacy-group-sues-u-s-treasury-over-tornado-cash-sanctions-11665610506?mod=article_inline", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "oppress a government’s own citizenry" + } + ] + }, + { + "type": "span", + "value": ". " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most glaring problems of decentralization is Web3 spaces' level of volatility. While money laundering has existed since the beginning of money (and would continue even if all the blockchains in the world were to vanish), there's enough money laundering, wash trading, and socially engineered theft on blockchain networks to lead regulators to sanction, " + }, + { + "url": "https://www.coindesk.com/policy/2022/08/21/arrest-of-tornado-cash-developer-draws-dutch-crypto-community-protest/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "or even imprison" + } + ] + }, + { + "type": "span", + "value": ", participants and even developers—while hinders true decentralization across the ecosystem. If we want to make the ecosystem safe for everyone, we've got a lot of housecleaning to do." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "So where does Laconic come in?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is a core feature of the Laconic Network, delivering all of the benefits we've described—uptime, security, affordability, censorship resistance, and fair, consensus-driven governance. The network's seven founding Members, and 60+ additional Members spread across multiple jurisdictions, each run their own hardware, making it highly resistant to concentrated server failures, tampering by individual bad actors, and hostile takeovers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Each Member serves the network by running " + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Watchers," + } + ] + }, + { + "type": "span", + "value": " smaller caches of specific data commonly used by, for example, DApp developers. There's no single point of failure—if any one Service Provider fails, other Members can ensure Watcher uptime." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization will drive even more benefits as the network matures. Laconic Network customers can purchase data directly from Service Providers, which compete to win customers through low prices, at the same time exerting downward pressure on data prices across the industry." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, while geographically diverse traditional networks can be subject to an incredibly complex web of national and local laws, participants in the decentralized Laconic Network need comply only with those laws governing their own jurisdictions. “In each jurisdiction,\" explains Dudley, \"varying and often contradictory regulations or sanctions can interfere with or contradict one another, making global compliance difficult. The goal is to be compliant in our jurisdiction, while allowing our users to be compliant in each of theirs.” Each Laconic Stack user decides how best to comply with the laws of their jurisdiction. The biggest benefit here, though, is that under this model, no single governing body has the power to determine a specific flow of data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is at the heart of Web3, and key to all major benefits of the blockchain. As a structure, it has the power to offer the most benefit to the largest group of people, making it more difficult for the few to hoard power or capital at the expense of the many. But for blockchain, and the larger Web3 ecosystem, to reach its full potential, we must work toward broad understanding of the benefits and pitfalls of decentralized networks. Only then can we begin to calm the waters of Web3, and remove the sharks for the next billion users we hope to bring with us." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "61461416", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Why Decentralization Matters ", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Why Decentralization Matters " + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Why Decentralization Matters " + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1669073122-central_decentral_009.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1669073122-central_decentral_009.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-11-21T23:35:25Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "how-laconic-different", + "title": "How is Laconic different?", + "date": "2022-07-26", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "24856058", + "name": "Maly Ly" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1657616658-laconic_differentiators_7.png" + }, + "content": { + "blocks": [ + { + "id": "38579992", + "text": "Laconic is a new indexing and querying solution that aims to make verifiable blockchain data available from a truly decentralized and massively scalable network. We are not the first project to address this problem, and this article will describe how our approach sets us apart from the work that has been done before." + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "item": "38579992", + "type": "block" + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Vision" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We believe that Web3 and blockchain technologies offer the promise of " + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "permissionless and equal access" + }, + { + "type": "span", + "value": " to new digital tools and financial instruments, and that these properties can be a force for positive change. Critically, decentralization of the networks and resources in Web3 is the key protection against the monopolistic jurisdiction of large tech companies and overzealous governments.  " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We further believe that the creation of public, consensus driven, cryptographically verifiable data leads to greater transparency and accountability. Finally, we believe that transacting on blockchains fosters incentive alignments through tokenomic engineering, allowing us to be more deterministic about how systems and society run." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These are the core values that imbue all of the design decisions in creating the Laconic Network. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The challenges facing Web3 adoption are significant, and threaten to prevent the fulfillment of the promise and vision. Laconic Network is focusing on a set of challenges that arise when DApp developers need to interact with blockchains and query the data that is stored there. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Working with blockchain data is more challenging than traditional web development because of the storage constraints, the overhead of preserving consensus and protecting networks from attack. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Other significant challenges include: " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Blockchain nodes are designed to be write-optimized and bear large overhead costs for storage, network gossip, and computation required for consensus and security." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "True internet scale growth (eg. comparable to Facebook or Twitter) is not possible on existing Web3 backend infrastructure. Mass adoption will require the development of a data scalability layer that facilitates for Web3 the types of usage patterns that we expect from Instagram, Google, and LinkedIn." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These challenges all compound when cross chain data is called for, which is increasingly the case. The data scalability layer will also need to account for cross-chain interoperability." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic isn’t the first to address the challenge of building a data availability layer for Web3. Companies such as Infura and Alchemy have long offered blockchain data services as convenient APIs for DApp developers, and have thus facilitated the early growth of high utility applications that interact with the underlying chains. As centralized services with quickly growing traction and clout, they already in some ways resemble the monopolistic centralized players of Web2. The convenience they offer is paid for at the price of losing censorship resistance and permissionless access. In short, we trust these companies to tell us the truth and treat us right. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Somewhat truer to the ethos of blockchain and Web3 are The Graph and Covalent, both of which have recently launched products that have elements of decentralization and verification of the data being served. For Ethereum, these services are limited to indexing events, transactions, and internal transactions, and don’t typically index the state and storage trees at all. One consequence of this is that they don’t support the Ethereum JSON RPC get_proof() call, which is essential to safeguarding the verifiability of the data being queried. No proof, no trustworthy data. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Why will Laconic’s solution solve current challenges?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network is innovating on three separate fronts: technology, governance, and incentive alignment. The challenge of serving verifiable blockchain data, at scale, from a decentralized platform is so enormous that a purely technical solution won’t suffice. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Technology" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "At a technology level, we have pioneered multiple innovations. " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD structures to preserve the hash-linked data format that is native to blockchain data—even withstanding transformations." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The concept of the Laconic Full Index Node which removes the need to re-index every time a new data access use case is materialized. The result is significantly faster setup times for customized data access API endpoints, that are tailored to individual DApps." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Re-indexing of blockchain data into specialized caches (Watchers) to simplify anticipated DApp use cases. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Content-addressability of blockchain data. In the same way that IPFS is used to request files by content hashes, blockchain data will be requested by hashes of its content, and served through the off-chain p2p IPFS network. " + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "This leads to a global hyperscale caching layer that is sufficient enough to ensure the viability of Facebook or Google level adoption of Web3." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Working with multiple international legal teams, Laconic Network is pioneering a novel governance model with the aims of ensuring unprecedented decentralization, fairness, and equality. The goals are simple: maintain a decentralized network that resists being monopolized and which is not subject to the whims of any single entity’s jurisprudence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In order to achieve those goals, a number of problems must be addressed:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A sufficient number of Validators and Service Providers to scale as Web3 grows" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A rich diversity of underlying network and data center dependencies (e.g. we aim to avoid ending up with a preponderance of machines running on AWS)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A diversity of jurisdictional locations for network Members" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A diversity of funding sources for network members" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All governance decisions occur on-chain" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All members have equal voting power" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "No early token allocations for VCs or founders" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Incentive Alignment" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the prime benefits of blockchains is the ability to prescribe token incentives that align with desirable behaviors. For example, validators are incentivized to do the work needed to secure the network and establish consensus about block generation. The Laconic Network has created a number of incentives to align the many roles involved towards an ever more efficient and growing data availability layer for Web3." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For data consumers, it’s important to have a true selection of Service Providers (node operators who run Watchers). Since Service Providers must compete with each other on the basis of price and service level, this benefits consumers by guaranteeing the best level of service for the lowest price." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Developers writing Watchers (to be run by Service Providers), are incentivized to create useful Watchers. There is a mechanism for rewarding developers when people choose to run and query the Watchers they have written. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Members have a strong incentive to grow the network to meet demand through the fulfillment of new member auctions. These auctions bring new liquidity to the network as well as new members who can increase network capacity by indexing and serving data. A bonding curve rewards existing members with an auction fee with the earliest members benefiting the most. However, there is a disincentive to growing the network too quickly, as earnings for Members will come from indexing and serving data. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Anyone interested in building out the network will have a token incentive and opportunity to participate simply by running an in-browser cache of the off-chain data. By caching the data in this way, it will form an infinitely scalable global hyper-cache of the data, and the service level of DApps will increase as a result. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Conclusion" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic aims to significantly contribute to the growth and adoption of Web3 and blockchain technologies by solving foundational problems that currently hamper the development of DApps. This involves indexing blockchain data and making it available from a decentralized network of service providers, and doing so in a way that retains the cryptographic verifiability of that data. The innovations that allow us to do this emanate not only from technological advances, but also from the novel governance and incentivisation structures that are built into the very network structure. " + } + ] + } + ] + } + } + }, + "featured": true, + "id": "35743439", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "How is Laconic different?", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "How is Laconic different?" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "How is Laconic different?" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1657616658-laconic_differentiators_7.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1657616658-laconic_differentiators_7.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-11-09T17:36:58Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "laconic-and-consensys-metamask-launch-mobymask-light-client", + "title": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client", + "date": "2022-11-08", + "category": [ + { + "id": "3545001", + "slug": "partners", + "title": "Partners" + } + ], + "author": { + "id": "24856058", + "name": "Maly Ly" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1667863936-partnership-template-updated.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "blockquote", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "\"It’s hard to overstate what an achievement this [Laconic] is for bringing down the computational costs of privacy, scalable data distribution, and also speed of data lookups for users who repeatedly use the same contracts but don’t necessarily run their own full nodes.\"" + } + ] + } + ], + "attribution": "Dan Finlay, MetaMask Founder" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Today I’m thrilled to announce that " + }, + { + "url": "http://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-clienthttps://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic is partnering with the world’s leading self-custodial wallet, ConsenSys’s MetaMask" + } + ] + }, + { + "type": "span", + "value": ", and MetaMask founder Dan Finlay, to launch " + }, + { + "url": "https://mobymask.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "MobyMask" + } + ] + }, + { + "type": "span", + "value": ", an anti-phishing tool. You can check out Dan Finlay’s " + }, + { + "url": "https://mirror.xyz/0x55e2780588aa5000F464f700D2676fD0a22Ee160/8whNch3m5KMzeo6g5eblcXMMplPf8UpW228cSh3nmzg", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "original MobyMask blog" + } + ] + }, + { + "type": "span", + "value": ", and his " + }, + { + "url": "https://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-client", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "blog announcement today" + } + ] + }, + { + "type": "span", + "value": ", for more information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MobyMask is a community-sourced registry for managing and reporting phishing accounts across social media and Web3 intersections, based on MetaMask's " + }, + { + "url": "https://mirror.xyz/0x55e2780588aa5000F464f700D2676fD0a22Ee160/pTIrlopsSUvWAbnq1qJDNKU1pGNLP8VEn1H8DSVcvXM", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Delegatable framework" + } + ] + }, + { + "type": "span", + "value": ". It provides robust tools for sourcing phishing reporters from across online communities, using a dynamic web of trust. The registry is designed to grow organically as community members report scammers and phishers directly onto the Ethereum mainnet." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Accessible, affordable privacy and security" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic's MobyMask-caching Ethereum light client greatly reduces the cost of hosting a trustworthy copy of the MobyMask anti-phishing registry, making the tool accessible and affordable for a wide range of users. By optimizing the blockchain query process, explains Dan Finlay in his blog announcement today, Laconic \"greatly reduces the computational costs of privacy, scalable data distribution, and also speed of data lookups for users who repeatedly use the same contracts but don’t necessarily run their own full nodes.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MobyMask further streamlines blockchain security by bringing light-client functionality directly to the browser. The key is the " + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watcher" + } + ] + }, + { + "type": "span", + "value": ", an essential piece of the Laconic Stack that caches only the specific blockchain data required for a particular query—reducing data requirements by orders of magnitude while creating a lightweight, self-hostable process that web services such as MetaMask, WalletGuard, and Phishfort can use to draw MobyMask phishing detection data. A soon-to-be-released TypeScript version of our Watcher will streamline security even further, with support for fully browser-based list caching and peer-to-peer data replication. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Lowering barriers for entry to Web3 " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Phishing and related scams plague both novice and experienced blockchain users, creating further barriers to adoption and limiting the potential for growth of the Web3 ecosystem. That’s created a real need for tools that make this ecosystem safer, faster, easier, and more affordable to use. In reducing technical requirements for interacting with the blockchain, the Laconic protocol lowers barriers to entry, making Web 3 technologies more accessible to those with limited resources." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With that goal in mind, Laconic is working on an update that lets users search a dynamic repository of phishing reports, and share them with a network of peers, via a free private API. Messages will be fully provable on chain, with blockchain used only to resolve registry conflicts and revoke access when needed. While the initial repository is by invitation only, Laconic and MobyMask plan to eventually allow users to subscribe to multiple roots of trust, and to host their own. Meanwhile, we’re laying the groundwork for an ambitious shared vision: a system with the ability to verify credible sources for a wide range of information types." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "A highly scalable and privacy-preserving application" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With MobyMask making Web3 access safer and less stressful for everyone involved, the potential benefits, says Dan, are substantial. ”If we’re going to build anything of value out of decentralized technology, we need to basically eliminate phishing. That's going to take a lot of creativity and ingenuity, and we’re happy that " + }, + { + "url": "https://www.laconic.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic" + } + ] + }, + { + "type": "span", + "value": " and MobyMask combine so well to deliver a highly scalable and privacy-preserving application whose safety remains rooted on the blockchain.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nYou can learn more about MobyMask at " + }, + { + "url": "http://www.mobymask.com", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "mobymask.com" + } + ] + }, + { + "type": "span", + "value": ". For more information on Laconic, " + }, + { + "url": "https://discord.com/invite/ukhbBemyxY", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "join the Laconic Discord" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "56055548", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1667863936-partnership-template-updated.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1667863936-partnership-template-updated.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-11-08T20:40:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "laconic-devcon-vi-recap", + "title": "Laconic’s Devcon VI Recap", + "date": "2022-10-20", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1666289454-devcon-blog.png" + }, + "content": { + "blocks": [ + { + "id": "55844240", + "title": "MEV" + }, + { + "id": "55844241", + "title": "OFAC" + }, + { + "id": "55844702", + "title": "Regulation" + }, + { + "id": "55846103", + "title": "Devcon" + }, + { + "id": "55844242", + "label": "Join Our Discord to Learn More", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As the first Devcon held in 3 years, and right after the historic Merge, Devcon VI drew thousands of attendees from around the world and our team was fortunate to be a part of it.\n" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So what were the hot topics at this year’s Devcon VI in Bogota?" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "\n" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "1. Maximum Extractable Value (MEV)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most pervasive topics at Devcon was MEV. Flashbots co-founder Phil Daian announced the launch of SUAVE, Single Unifying Auction for Value Expression, to combat MEV centralization and censorship.\n\nSUAVE will be an MEV-aware encrypted mempool that maximizes profits for users. " + } + ] + }, + { + "item": "55844240", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "2. Office of Foreign Assets Control (OFAC)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Post Merge, OFAC compliant MEV boost relays have been enabled across a broader array of block proposers resulting in a censorship of 51% of Ethereum blocks. The implication is that if block producers can censor tx’s due to OFAC compliance, what other types of transactions could they censor?" + } + ] + }, + { + "item": "55844241", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "3. Regulation: Digital Commodity Consumer Protection Act (DCCPA) " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Regulation isn’t keeping up with the speed of innovation. This is obvious for instance in Europe’s current attempt to to regulate dollar-pegged stablecoins, capping volumes and potentially banning algo-stablecoin use. Stablecoin volume has increased dramatically over the last several years and a cap or ban could significantly hurt the crypto ecosystem overall.\n\nThe Digital Commodity Consumer Protection Act (DCCPA) could have a significant impact on DeFi in the US as well. The general consensus is that the crypto community as a whole has to advocate for positive outcomes, and resource groups like Coin Center that are actively fighting for regulatory guidance. " + } + ] + }, + { + "item": "55844702", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "5. Layer 2" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unsurprisingly, the increasingly crowded and competitive L2 landscape was on display.–The Arbitrum and Optimism booths  angled for attention side by side at the conference, an example of the escalating war for dominance between the major Layer 2 solutions. There was discussion around the security tradeoffs between side chains like Polygon and rollups like Arbitrum & Optimism, along with concerns that rollups are currently too centralized and lacking fraud proofs. " + }, + { + "url": "https://www.laconic.com/blog/what-is-a-proof", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Laconic knows a thing or two about proofs" + } + ] + }, + { + "type": "span", + "value": "…\n" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "6. Data Scaling" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Data indexing and scaling continues to be a fundamental challenge for Web3 development. You’re either running a full node yourself or forced to make compromises while using one of a handful of existing centralized indexing solutions.\nEnter Laconic, the first multichain verifiable data indexer. Laconic enables developers to build internet-scale Web3 applications and light clients at a fraction of the speed and the cost of current tools." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "*Side note, the conference wifi pw was “runfafullnode” but we’re here to do that for you…IYKYK.\n" + } + ] + }, + { + "item": "55846103", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Interestingly, the decision to host the conference in South America was prescient as the economic crises in Venezuela and Argentina highlight the use cases of crypto as a hedge against inflation and more generally as a means for financial sovereignty. We spoke with a number of Argentinians and Venezuelans about the growing usage of crypto payments in their countries.\n\nThe need to enable developers to build applications with access to faster and more reliable data has never been greater. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "item": "55844242", + "type": "block" + } + ] + } + } + }, + "featured": false, + "id": "55844243", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Laconic’s Devcon VI Recap", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Laconic’s Devcon VI Recap" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Laconic’s Devcon VI Recap" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1666289454-devcon-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1666289454-devcon-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-10-20T18:53:41Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "how-laconic-improves-the-nft-experience", + "title": "How Laconic Radically Improves the NFT Experience", + "date": "2022-10-05", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1664922912-laconic_nfts_003-2.png" + }, + "content": { + "blocks": [ + { + "id": "55708950", + "label": "Discord", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Part 1 of this two-part series discussed the five major implementation and integration issues plaguing DApp developers, along with an overview of how Laconic addresses each one:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "1. NFT data lacks consistency and integrity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "2. NFT data is fragmented and scattered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "3. For both NFTs and equivalent token formats, data types vary from blockchain to blockchain." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "4. NFT games require lightning-fast retrieval and scalability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "5. Data retrieved from multiple locations is difficult to verify. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now, in Part 2, we'll explore how these problems are playing out in the real world—and how Laconic tools and processes shield both users and developers from the effects of shifting and conflicting standards, radically reducing everyone's list of problems.   " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Apes, witches ... tax fraud? The power of caching." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's start by looking at a current cornerstone of the industry. Arguably the most popular use case for NFT artwork is " + }, + { + "url": "https://boredapeyachtclub.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "10k PFP collections" + } + ] + }, + { + "type": "span", + "value": ", collections of (typically 10,000) relatively simple anthropomorphized cartoon jpegs, generated by combining variations on a large but limited set of defined metadata “traits,” each of which correlates to a physical attribute of the generated character drawing. " + }, + { + "url": "https://boredapeyachtclub.com/#/home", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Bored Ape Yacht Club" + } + ] + }, + { + "type": "span", + "value": ", for example, uses this model. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the case of another popular collection, " + }, + { + "url": "https://www.cryptocoven.xyz/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Crypto Coven" + } + ] + }, + { + "type": "span", + "value": ", a smart contract is called to mint tokens based on a similar list of randomly generated traits. To gather each piece of this data and display it, a wallet client or platform makes an API call to an " + }, + { + "url": "https://docs.metamask.io/guide/rpc-api.html#table-of-contents", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "RPC" + } + ] + }, + { + "type": "span", + "value": " provider. To retrieve the data, the client or platform must sift through an extraordinary amount of data to find just a few essential pieces. And all that searching eventually adds up. More serious problems could also arise, from sandwich MEVs to lowball ape sales as a " + }, + { + "url": "https://www.cryptonews.net/news/nft/6583225/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "front for tax shenanigans" + } + ] + }, + { + "type": "span", + "value": ".\n\nLuckily, there's an easier (and less easily abused) option. A Laconic Watcher could cache all data related to a given smart contract—in this case, " + }, + { + "url": "https://cryptocoven.mirror.xyz/A622VSRm8-9oLzc8l3oFGmfnFUZQmDQ3Wx3ObhSlhsc", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the brilliant Crypto Coven `counters.sol` variant" + } + ] + }, + { + "type": "span", + "value": "—making it instantly available at any DApp request while preserving proof of authenticity. Because Laconic can prove metadata at the current block height, not several blocks behind like most current centralized data providers, all NFT data received is guaranteed correct. That makes for faster queries, lower costs, easier updates—a quality-of-life upgrade for developers. For a particularly busy smart contract such as Bored Ape Yacht Club, Laconic's data cache could also drastically improve the daily experience of thousands of users. And let's not forget the damage done to industry reputation by " + }, + { + "url": "https://nftevening.com/bored-ape-6462-sold-for-200/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the successful exploits of every \"fat-fingered\" Ape owner" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Where we’re going, we don’t need standards." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Smart contracts are incredibly popular for good reason. But there are " + }, + { + "url": "https://etherscan.io/address/0x53e4c0167ed855e96f562dbb911854d586f5cc07#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "countless" + } + ] + }, + { + "type": "span", + "value": " " + }, + { + "url": "https://etherscan.io/address/0x517e643f53eb3622fd2c3a12c6bfde5e7bc8d5ca#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "variations" + } + ] + }, + { + "type": "span", + "value": " out there, even on Ethereum. In fact, " + }, + { + "url": "https://www.smartcontractresearch.org/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "entire Web2 and Web3 developer communities" + } + ] + }, + { + "type": "span", + "value": " view this aspect of NFT technology as where its real beauty is revealed: the developer as artist, Making It New. Limit contract and metadata standards, the argument goes, and you'll hamstring developers' creativity, starving the ecosystem of innovative ideas. (Unaddressed standards are, of course, nothing new. Think back to the browser wars of Web 1.0, when entire websites ... just didn't work on Internet Explorer, and at many design shops, ensuring IE functionality would cost you a good 20% extra.)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is uniquely suited to solve the problems associated with nonstandardized token data retrieval. By caching token data into microchains of IPLD blocks, and serving it directly to DApps from the resulting decentralized content-addressable database in a unified format, the Laconic Watcher makes the entire issue moot. It displays the precise data queried, confirms that it's correct—and does so more quickly and with a more current state than any standard RPC service." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "An end run around fragmented and scattered metadata." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A Laconic Watcher makes it easy for developers to retrieve blockchain data from an indexed database; fetch off-chain data as needed; and correlate, merkleize, and cache the results. With a " + }, + { + "url": "https://etherscan.io/address/0xe6ddda1c3f1cb01aa5c86a21e8636deabfd1f013#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Crypto Coven smart contract" + } + ] + }, + { + "type": "span", + "value": ", for example, there's no need to concern yourself with both the smart contract and the media the tokenURI points to—a Watcher can tie them together in ways standard RPC providers can't, with a terrific signal-to-noise ratio, perfectly up-to-date state, and persistent uptime.\n\nWatchers define and expose precisely crafted APIs for DApps that consume specific data sets, such as a given smart contract. Allowing a DApp itself to focus solely on its primary mission of delivering the goods to the user eliminates the need for (often surprisingly heavy) query lifts." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Smooth token data retrieval and management, even from multiple blockchains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Non-fungible tokens exist on multiple blockchains, in varying degrees of complexity, in a range of programming languages, in line with wildly varying (if any) metadata standards. The result is, unsurprisingly, significant friction when it comes to token management. Laconic removes much of the drag by unifying and extending the possibilities of metadata once it’s been retrieved." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To maintain a unified format regardless of chain or data source, Laconic stores all blockchain data in content-addressable data formats. This allows for a level of global availability and extensibility that makes DApp development far more convenient. " + }, + { + "url": "https://www.laconic.com/blog/99-problems-but-nfts-aint-one", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Our hypothetical on-chain Library of Alexandria in Part 1" + } + ] + }, + { + "type": "span", + "value": " is made possible by Laconic’s uniquely decentralized, content-addressable format and the Watcher's computationally light footprint. Think of this chain-agnostic view as a Rosetta Stone for Web3 data." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic makes querying NFT data verifiable, fast, and affordable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic makes DApp access both faster and easier with its up-to-date, indexed, verifiable blockchain data and custom query and caching services; queries, too, become more affordable to develop and maintain. And in a time of ballooning NFT data stores and rapidly increasing demands on user experience, Laconic Watchers deliver data at a fraction of the cost of traditional data retrieval." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In short, the architecture of the Laconic Network delivers practical solutions to many of today's most pressing NFT challenges. Custom Laconic Watcher services can, for example:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collect data from multiple blockchains" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Process that data to make it consumable by DApps" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Preserve data verifiability across transformations " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Keep data up to date" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Streamline DApp data retrieval" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With these capabilities, the Laconic Network becomes an indispensable data querying and verification layer for any NFT-related service—and, more than any other approach to querying blockchain data, a resource with the capacity to help the entire industry thrive." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Read more about Watchers and the Laconic Network " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "here" + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Got questions? Join us on Discord." + } + ] + }, + { + "item": "55708950", + "type": "block" + } + ] + } + } + }, + "featured": true, + "id": "55708951", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "How Laconic Radically Improves the NFT Experience", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "How Laconic Radically Improves the NFT Experience" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "How Laconic Radically Improves the NFT Experience" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1664922912-laconic_nfts_003-2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1664922912-laconic_nfts_003-2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-10-05T15:24:49Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "99-problems-but-nfts-aint-one", + "title": "99 Problems But NFTs Ain’t One", + "date": "2022-09-28", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1664296930-nfts005-1.png" + }, + "content": { + "blocks": [ + { + "id": "55644653", + "title": "Tweet" + }, + { + "id": "55644654", + "label": "Discord", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ask most people what they know about NFTs, and it’s likely you’ll hear a lot about digital art, tokenized authenticity, proof of ownership, and/or a hot new asset class (or, depending who you hang out with, commodity fetishism and the death knell of late-stage capitalism). But hot takes aside, today’s market for generative artwork is only the tip of the iceberg. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whatever the use case—and the possibilities are vast—today’s NFT ecosystem is generating mountains of largely unstructured data, both on and off chain. And without established technical standards to ensure clear structure and verifiability, it’s all too easy for that data to become the stuff of developer nightmares." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "More possibilities, more problems." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As use cases for NFTs continue to expand beyond " + }, + { + "url": "https://superrare.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "1/1 works of art" + } + ] + }, + { + "type": "span", + "value": " and " + }, + { + "url": "https://boredapeyachtclub.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "10k pfp collections" + } + ] + }, + { + "type": "span", + "value": ", developers building applications that integrate on-chain tokens face a widening maze of challenges around data management and queries. . The power of an NFT lies in its ability to represent any unique entity, with today’s common use cases including in-game tokens, editioned generative artwork, event ticketing, and even " + }, + { + "url": "https://www.theblock.co/post/134923/artist-blows-up-lamborghini-to-make-nfts-in-protest-against-crypto-culture", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "physical works or goods" + } + ] + }, + { + "type": "span", + "value": ". Blockchain and non-fungible tokens, in this case, make use of smart contracts, which can be written in a variety of programming languages, using varying methods of metadata storage and retrieval. The digital asset to which a given piece of metadata refers, and the metadata itself, can each be located almost anywhere in the decentralized web—which is itself both immeasurably large and continuously expanding. \n\nCombine a diversity of programming languages and an absence of standardization, and you end up with some novel problems, mostly involving how to handle the sheer volume of data generated, how to locate it in the many places it might be stored (both on chain and off), and how to handle inconsistent data formats and structures. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network was created to address these challenges through shared standards that make it possible for DApp developers to quickly and intuitively integrate and manage disparate NFT data and assets. In this piece, the first of a two-part series, we look at five major NFT implementation and integration issues—along with what we’re doing to solve each one, so you can sleep at night. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "1. NFT data lacks consistency and integrity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The functionality of an NFT lies in the metadata describing the individual item it represents. That metadata can consist of traits describing the characteristics of a jpeg artwork, essential information about copyright and intellectual property, guidelines for the item’s intended presentation, or all of the above and more. Metadata can also address a broad set of questions:" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": " Am I allowed to use an NFT for commercial purposes based on IP and copyright? Can I play a specific video, given its file format and codec? Are there readable methods for rendering this generative work? Can I easily list this NFT on the larger NFT marketplaces, where I have the best chance of selling it for the best price and in a timely manner?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The rapid growth of the NFT market has further expanded the possible functions of metadata. Developers need an efficient way to retrieve all types of metadata, and to maintain correlation with their on-chain counterparts in provable, hash-linked data structures. Laconic Watchers—APIs that serve data from the Laconic Network—fill this need. The Watchers’ custom search and caching services collect variously constructed data and combine it into a unified form that DApps can interpret and use, without sacrificing data integrity." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "2. NFT data is fragmented and scattered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s not practical to use current blockchain technology as a data storage or retrieval protocol. It was designed primarily as a means for achieving trustless consensus, not as a data availability system. The assets to which NFTs refer are often stored in protocols such as " + }, + { + "url": "https://www.arweave.org/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Arweave" + } + ] + }, + { + "type": "span", + "value": " and " + }, + { + "url": "https://ipfs.tech/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "IPFS" + } + ] + }, + { + "type": "span", + "value": ", and pointed to by the NFT’s “" + }, + { + "url": "https://docs.openzeppelin.com/contracts/2.x/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "tokenURI" + } + ] + }, + { + "type": "span", + "value": ".”  In one recent example, complete rendering libraries are stored as " + }, + { + "url": "https://twitter.com/dhof/status/1569509636587528195?s=20&t=KUYxKh-vG7qFaTtCvhRObA", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "compressed, on-chain data URIs" + } + ] + }, + { + "type": "span", + "value": "; smart contracts then access these libraries to render fully on-chain generative artworks." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A problem emerges, however, whenever a DApp needs to access any of this information. Methods for retrieval and DApp ingestion vary wildly depending on data type and location. And while RPC services can locate data—for a price—in most cases there’s no measurable way to ensure its integrity. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Despite being the most lightweight element of the Laconic Stack, the Laconic Watcher has the power to alleviate the proof issue, by preserving evidence of proof across data transformations while querying a far smaller subset of data than is typically required." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "3. For both NFTs and equivalent token formats, data types vary from blockchain to blockchain." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Most of today’s NFTs are based on Ethereum, and most commonly written in Solidity. But zoom out for a wider view of the possibilities for both token types and blockchains, and the problem space increases correspondingly. On Tezos, for instance, smart contracts are most often written in SmartPy or LIGO, with third-place Michelson being a common low-level, domain-specific language. On Solana, Rust, C, and C++ are commonly used to compose smart contracts (referred to in the Solana ecosystem as “programs).” It’s safe to say that we have more than a small naming, language, and methodology mess on our hands in the blockchain ecosystem!\n\nLet’s imagine a DApp that tracks all NFTs representing a certain kind of media—books, for example—to provide an index of on-chain literature. It would need the ability to accurately interpret smart contracts from each chain, across widely varying programming languages, syntax, and metadata standards." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network drastically simplifies this process, offering developers a unified view of data while allowing DApps to agnostically query data from multiple blockchains from a decentralized, content-addressable database." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "4. NFT games require lightning-fast retrieval and scalability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The volume of NFT transactions is expected to rise significantly with the " + }, + { + "url": "https://tiktok.immutable.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "current influx" + } + ] + }, + { + "type": "span", + "value": " of " + }, + { + "url": "https://about.instagram.com/blog/announcements/instagram-digital-collectibles", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "institutional Web 2 players" + } + ] + }, + { + "type": "span", + "value": " into the Web 3 ecosystem. The problem: As transaction volume and speed increase, data availability with censorship resistance and proof of integrity becomes increasingly unsustainable. And blockchain-based games alone are poised to send NFT transaction volumes to stratospheric heights, with more and more in-game events and transactions driving mounting network traffic. For example, the popular game " + }, + { + "url": "https://godsunchained.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Gods Unchained" + } + ] + }, + { + "type": "span", + "value": " is already generating " + }, + { + "url": "https://chainplay.gg/games/gods-unchained/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "significant transaction volume" + } + ] + }, + { + "type": "span", + "value": "—and it’s just one of countless on-chain games. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The growth of games and social apps using on-chain transactions continues to expose issues with Ethereum’s scalability and speed. The problem is compounded by the fact that most indexing services are typically a few blocks behind with updates—too far back to have DApps react to in-game events on time. And that leaves players holding the bag, subjected to unnecessarily clunky and unwieldy gaming experiences." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike traditional blockchain indexing services, the Laconic Network is equipped to provide up-to-date blockchain data with trivial delay, for scenarios in which real-time data retrieval is essential to user experience." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "5. Data retrieved from multiple locations is difficult to verify. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A fundamental promise of blockchain is verifiability. And while on-chain data is verifiable " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "per se" + }, + { + "type": "span", + "value": ", NFT data can be stored in any number of data repositories. Meanwhile, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "using" + }, + { + "type": "span", + "value": " that data requires intermediaries such as DNS system records, traditional web servers, and files stored in Web2 datacenters. Every one of these exposes the data to the possibility of censorship, manipulation by bad-faith actors, or simple disappearance. In such scenarios, associated NFT data is most often tied to a token via a ”tokenURI” that references the location of a JSON file containing token metadata. That file, in turn,  lives in one of these off-chain data storage protocols.\n\nTypical " + }, + { + "url": "https://ethereum.org/en/developers/docs/apis/json-rpc/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "RPC" + } + ] + }, + { + "type": "span", + "value": " providers can retrieve information about such data, then provide it to a client. A wallet client, for example, can find any media files associated with a particular NFT and display them. But this creates a trust bottleneck in the Web 3 ecosystem—requiring DApps and their users to trust the source of the data without proof. Solving this trust point is one of the primary goals of Laconic." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Today’s NFT ecosystem isn’t providing standards. Enter Laconic." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s clear that across blockchains, tokenization standards vary widely. Even with similar token types on the same chain, we see countless examples of how and where data is stored:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": " " + } + ] + }, + { + "item": "55644653", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nStay tuned for Part 2, where we’ll dive into more detail on how Laconic solves common implementation and integration challenges, offering both collectors and developers a far smoother NFT experience. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Got questions? Join us on Discord.\n" + } + ] + }, + { + "item": "55644654", + "type": "block" + } + ] + } + } + }, + "featured": false, + "id": "55644655", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "99 Problems But NFTs Ain’t One", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "99 Problems But NFTs Ain’t One" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "99 Problems But NFTs Ain’t One" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1664296930-nfts005-1.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1664296930-nfts005-1.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-09-27T16:47:13Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "what-is-a-proof", + "title": "What Is a Proof and Why Do You Need One?", + "date": "2022-09-20", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55512259", + "name": "Stefan Adolf" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1663659141-laconic_proof_blog_2.png" + }, + "content": { + "blocks": [ + { + "id": "55510966", + "title": "Merkle tree" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralized apps rely heavily on blockchain state to display current and historical values like an account's balance, its NFT holdings, or current exchange rates on DEXes. Projects like The Graph or Covalent make blockchain data accessible, chain indexers like Etherscan provide historical information about the chain's state, and RPC providers like Infura or Alchemy allow wallet extensions like Metamask to connect to their full-node infrastructure. However, unless you run your very own full-node you cannot prove that any value provided by these sources truly resembles the chain state they represent. In short: relying on external, centralized services imposes a trust risk. However, the way Ethereum stores data allows trusting the chain state data without trusting the provider itself, thanks to proofs. This is how it works." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Ethereum's storage model" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ethereum uses a tree-based storage model that contains each account's balance and the storage for contract-based accounts. All values are wrapped in a traversable hash trie structure - a " + }, + { + "url": "https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Patricia Merkle Trie" + } + ] + }, + { + "type": "span", + "value": " - that allows creating cryptographic hash proofs for each tree node. The state root hash of a block represents the complete state of all accounts and contracts at a given block height, aka the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "world" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "state" + }, + { + "type": "span", + "value": ". Block producers execute new transactions on their local state copy and bundle the resulting new state root hash and a list of all executed transactions into a new block." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Light Ethereum clients cannot verify the current chain's state since they're not keeping a full copy of the world state. Full nodes can rebuild the state tree by executing every transaction since genesis, and then constantly update it as new blocks arrive. To save storage space they only keep the most recent chain states and hence cannot respond to state queries from the past, i.e. they're not able to determine an account's balance older than - depending on the node's settings - 64 blocks. That's sufficient to create proofs about the current state, though." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cryptographic proofs on hash trees" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Merkle trees use hashes of their nodes' values as keys to guarantee their content integrity. To build a simple binary Merkle tree, a prover starts by computing a hash over a node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "L" + }, + { + "type": "span", + "value": "'s content " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": ". Next, they compute the hash over " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": " and the hash of a sibling content node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "K" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": ": " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(h(K)+h(L))" + }, + { + "type": "span", + "value": ". Using this hash as a key they create a new node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "KL" + }, + { + "type": "span", + "value": " with " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(K)" + }, + { + "type": "span", + "value": " and " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": " as children and find another node" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "IJ" + }, + { + "type": "span", + "value": " at the same tree level to create a new parent node that hashes all values of the underlying tree structure: " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(IJ + KL) = h(h(h(I) + h(J)) + h(h(K) + h(L)))" + }, + { + "type": "span", + "value": ". This process is repeated until all nodes are combined to a single root hash. Since each parent node keeps a hash of its children, it's impossible to change anything down the tree without affecting the tree's root node." + } + ] + }, + { + "item": "55510966", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove that a certain base value has been incorporated into a Merkle tree's root, one collects the hashes of sibling nodes at each level of the tree. A prover successively computes their hash sums to finally recreate the root hash, thereby proving the inclusion of the node's value." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A simple way of storing binary Merkle trees in a flat key/value database structure is with " + }, + { + "url": "https://en.wikipedia.org/wiki/Radix_tree", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "radix tries" + } + ] + }, + { + "type": "span", + "value": ". To store a node, one splits its hexadecimal hash key into its nibbles (the hex characters) and create subdirectories for each of them. A node with the key " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "0xcafe" + }, + { + "type": "span", + "value": " would end up in a folder structure like " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "c/a/f/e/value" + }, + { + "type": "span", + "value": ". Looking up a node requires traversing the directory structure along the key's nibbles down to the last nibble's directory where the node's value will be stored. While simple to understand this approach is far too inefficient to store large tree data structures like Ethereum's world state. The network instead uses Patricia Merkle Tries that add some complexity to the data structure by introducing branch-, extension-, leaf- and null-nodes but are way more efficient to store and traverse. Additionally, a Recursive Length Prefix (" + }, + { + "url": "https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "RLP" + } + ] + }, + { + "type": "span", + "value": ") encoding is used to serialize nested arrays and data structures." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Proving account state" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ethereum's state trie consists of a mapping between account addresses and their state, defined by their current " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "nonce" + }, + { + "type": "span", + "value": " and " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "balance" + }, + { + "type": "span", + "value": " and in the case of contract accounts a " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "storageRoot" + }, + { + "type": "span", + "value": " key that points to a position in the dedicated contract storage trie and their binary code hash. Just using a block's " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " one can look up an account's current state at that height using a full node's LevelDB (Geth's default storage database). Our examples are using an archive node of Ethereum's recently launched " + }, + { + "url": "https://sepolia.dev/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Sepolia testnet" + } + ] + }, + { + "type": "span", + "value": " because it's still small enough to be synced to a developer's machine." + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst {\n toBuffer,\n bufferToHex,\n keccak256,\n Account,\n} = require(\"ethereumjs-util\");\nconst Web3 = require(\"web3\");\n\nconst web3 = new Web3(\"http://127.0.0.1:8545\");\nconst db = new Level(\".ethereum/sepolia/geth/chaindata\");\n\nasync function getState(address, blockNumber) {\n const block = await web3.eth.getBlock(blockNumber);\n const trie = new SecureTrie(db, toBuffer(block.stateRoot));\n\n //retrieves the account node's *value*\n const rawValue = await trie.get(toBuffer(address));\n const account = Account.fromRlpSerializedAccount(rawValue);\n console.log(account);\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "getState(\"0xe127a39da6ea2d7b1979372ae973a20bab08a80a\", 1432400)" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "value": "yields:" + } + ] + }, + { + "code": "account Account {\n nonce: ,\n balance: ,\n stateRoot: ,\n codeHash: \n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The provided account is an EOA, hence the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "codeHash" + }, + { + "type": "span", + "value": " value corresponds to " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "keccak256([])" + }, + { + "type": "span", + "value": " (another expression of a null value) and " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " is the hash of an RLP encoded zero:" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "keccak256(rlp.encode(0))" + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To create a proof of the account node's value against a block's state root you walk down the patricia trie starting at the block's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " node, resolve the paths on the trie's branch and extension nodes according to the signposts expressed by the leaf node's nibbles until the wanted leaf node is reached. On the way we're keeping track of all nodes we see while traversing the path. More details about " + }, + { + "url": "https://github.com/ethereumjs/ethereumjs-monorepo/blob/cfd7b7754490b072a035cceaba59c3dfb517effd/packages/trie/src/trie/trie.ts#L154", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the traversal can be found here" + } + ] + }, + { + "type": "span", + "value": ". We're using only high level methods to show proof creation here:" + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst { toBuffer, keccak256 } = require(\"ethereumjs-util\");\n\nconst db = new Level(\".ethereum/sepolia/geth/chaindata\");\nconst trie = new SecureTrie(db, toBuffer(block.stateRoot));\n\n//create a proof by finding the path on our own:\nconst { node: accountNode, stack } = await trie.findPath(\n keccak256(toBuffer(address))\n);\nconst proof = stack.map((stackElem) => {\n return stackElem.serialize();\n});\n\n//or by using a trie's convenience method:\nconst proof = await SecureTrie.createProof(trie, toBuffer(address));\n\n//or by using the ethereum node's RPC interface\nconst { accountProof: proof } = await web3.eth.getProof(\n address,\n [0],\n block.number\n);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All three proofs contain the same array of RLP encoded proof nodes, with our account node at its end:" + } + ] + }, + { + "code": "[\n \"0xf90211a078400dd6bd6f6cc13cdde5b707bdb6b9802fbd11809cba8aab2ee72f6920bbf3a0d88879eb6f43ea2d9c07538601e041aa46d64c87df68296a552ad12f8e5bebc0a044d40ac93c1bce308feda36a7712a38d9508087f04e612b1411bc57f80fa77a8a0b92c5fc0dcbc4a7982563c3667b940117770965e5f447578e4bde4fefcbb6d5aa0bc555b313ac90177e117dc10917581dad616770625e0956feb0df75a4be2ce63a0bd19008ae54cf1af36e59d7c92c553693dd468a0ef86c89401d01fc84aa4b751a0ee549b7ab8dd9a565ee6507bb46b9033e57b3a07541a4085763207911633367ca05652bd80f5b970a09c2090c38b13afa1772f3d97422c9a16548acdf629c35113a0a2e78f2d0da2d7218535b94bd69c4fad4425ff3bb22304d722185cf99dac2596a0a9470e771ae9cec5535b057948e68c4c38d8b8c1c2d8ed180d8f5244c63e422ea0726d10a2a5c85b156105414e75591611d896b8aaebd7fc92535845445a8baa81a0fdfb5aa2c35136eab67c3539d3ba34648bb8542b57e745976d52bca263ab19d7a01eafa4a1f59d28009a03b24d9a11c878faa78fe7ef3ecc4ef8891a77fea3fa35a02c980b0a841d5ec1978ae63adf7ba2727ac125f792f545cb0146d8df46adbdbfa052b97e4692fd69b4bebc6ceca258032319b07aca2ee62b504285031670fbc073a02c415e41b6bf838a8b742123fda110703ffdedea8834bb5113ae26f81887668e80\",\n \"0xf90211a098e0b7071d2b8d890cca479dba9f5b697d639d70d51f0312c5b8a27afc31a23da060759f93f482f66b233480a4cd060372567752b9dae507045b266600ab9b290da0c35bb5b2aae7eb8bc75a9de125daab9caa7d6d39e403153c9b34e51dcde46f78a097a4c1ba8ac42378481754eac9306b62d3d176602098820cb87e3c7f4091734da074b2785ca9199d0c68e2fa6dd01dcc24791887616adf8043cda56651cff25e87a00937963d57bc119144ebeff9bb99dcbef11aed563fd41818aaf0de89d15364b3a000ba5def8acd7c1c2af85ef49bc5d144fa1ceef93e81e9e64ad6457ab92cb569a069cd4b3ac6b03b1928f50840f81086ff0c819d1cbd445db0e5c306cfce9c0728a0a78339d0b9405da5a7943a6ac97b9585767a6f9f0e481b5e197d8fd820ac1cfaa019e1319ade8f1c2254dcef0b68483240044bc6bee2ea8197de895eb427068b1aa0ef36884df92b5769e521c6c0feaead503a8d70d11f493b3e6c8170ac917ced92a079c1ffdaa2a8d4ddacac77f47b43f1553c634300d23a8003574ec90874547cbaa0de4f130f1fd52baf5229e3871394b6f0562c64576039c6c38d06c695b7d87b07a09668798efa8c1e9b860d9f4a49b65db83c22fefd92f703a5875b87c7ae11bdf4a087ca8f145dbc9ad7f88468dbec0627d77ced63a508b4d7a5d28439e89c6be8e4a04fdb75be4fce4d76e41578f0bbcfdc2ded74d59855c9f3afc73c3659b9e28aa980\",\n \"0xf90211a058d99773cf489bd86bac2c6452c20cc7056f3505d7f96d83480cf85336d0da85a07dc4216585f73a213fb1828076cf7d93d8ce95e1981a5c8eb11d7166af8433eba0f85f31a95a1f83516719ae596266afdc529aa4ff9af8f0f6c1d7da6751f07d70a0f1371490b8ef31ce814673e9ecf0f498f2ec9eedcd554e93162d5c0e99e77d69a01d9709211cc089eba15fe3c052539203d11235b219e1c72e0172c9bec1d317bea09e907ad084233149719b5c6f8b4777a617b41744a0c3e9f6ef002970465a0656a044f3abc34546455ba2ed3d64ead41c734d5f56c240f8f8adfd971179538ca0efa08403716832691c8867b1366421b703925ff28e45d6f4360f898b70461e29d4eda0d8f4711ffe7bc79040b2f397523786a67d72814e8af9b65c9e03c318bb0eba48a0db84d2a268e67d6e45af7cd18a92fab1df496180e9ba8f0f0b620ab6344457cba0d01707094316206d689d80ed2684cda3aadd72ee2d4efbe1437bb194aa0adc87a0355e0e203341ac995bf2d90937f1ce807a13624e9d3831476f3205838b84f191a083961a9b498b791ee8e6a110a87aca2f2cea629df0827f4fbd168567d7246207a05d312338764151dc1936485637367ad9d9c440b4b0dde368dec4dbd5aaf51f4ea076b77567690221701e2edfff06f1c84a25b3181e4c8aa3bed2b3e3797a883564a0975b63f1be5615a4a68ec7f313abc0784def11a352975c40e886e03df95f764e80\",\n \"0xf9013180a0944968a4a8c7ba1e91cbb2414471a67f16c74e2ef6ac87447af0402520aed9c88080a03f3e1fed1008e242b89ce5b56106a091f946826c539f23ecee74c5b36b5d38cca041b58c41b5435e10c9b9f6b1f1bdf3d5e0294dfcfbf59b7a8c80475249e1d9e6a0d0b42862dcb175e47bf17614bd251b17cdab2710ba13175fd9b8c17afdc0893ba0ff4dc7ee613a7805802e50d39c3c8d8b35db63ceda7371502c5d119eca0a7b1780a0cd328766d44f930edc3a32ae5fd2a791f0de4dbabe014c4be77fc63b4b427310a0c20a6a1812f18d0c446408f90b09d0b592a07c45d06e780b49c6ad68e7c92bc5a084688c931305a75246f9d527a07483cff6e9ffbe4746a22ca65e07d3b3c6045f8080a0515f82b6e34b4713edead2652f266fdcea3729d64217113db564e918a9b1ed408080\",\n \"0xf8518080a06b91f274ef06ab455966416f6bb3779519bf72c68e85ec4b61ff8b99aa73a1818080a0937ee4540dc495eaa3cd61e12a4f6158a6be147d71ac3955a50ecd080e9732698080808080808080808080\",\n \"0xf8709e3e0484bbc22108bc77412e65255ce0387dc7c6a8d1917625ddb60ccbd98fb84ff84d028901f3d52b3c4f92e45da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"\n]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To verify this proof one doesn't need to have access to a full state tree at that block height. It's sufficient to know (and trust, e.g. by running a light node) the proof block's " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst { toBuffer, keccak256, Account } = require(\"ethereumjs-util\");\n\n// look Ma, no ethereum node needed\n// @param stateRoot hexString: the block's stateRoot\nasync function verifyAccountProof(address, proof, stateRoot) {\n const proofBufs = proof.map((p) => toBuffer(p));\n //build a new trie using only the proof nodes. They will hash towards the block's stateRoot\n const proofTrie = await SecureTrie.fromProof(proofBufs);\n\n //the account node can also be retrieved from the partial trie:\n const accNodeRaw = await proofTrie.get(keccak256(toBuffer(address)));\n const account = Account.fromRlpSerializedAccount(accNodeRaw);\n\n console.log(\"proven value\", account);\n const valid = await proofTrie.checkRoot(toBuffer(stateRoot));\n //or: const valid = bufferToHex(proofTrie.root) == stateRoot;\n return valid;\n}", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Proving contract state" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Proofs over contract state are created accordingly. They are rooted at the contract account's own " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " member variable that points to the mutable root of the contract's storage trie. Storage slots are addressed by their position hash as outlined in the " + }, + { + "url": "https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html#mappings-and-dynamic-arrays", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Solidity documentation" + } + ] + }, + { + "type": "span", + "value": " and are stored as leaf nodes in the storage trie. To simplify this procedure, we're going to use " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "eth_getProof" + }, + { + "type": "span", + "value": " calls here as described in " + }, + { + "url": "https://eips.ethereum.org/EIPS/eip-1186", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "EIP-1186" + } + ] + }, + { + "type": "span", + "value": " and supported by all major chain node implementations and service providers. Lets prove " + }, + { + "url": "https://sepolia.etherscan.io/address/0xF492600AeD292b1B94A1ba0CD29fB6ed6d6ab872", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this contract" + } + ] + }, + { + "type": "span", + "value": "'s " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "greeting" + }, + { + "type": "span", + "value": " to be \"" + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "Hello, Hardhat!" + }, + { + "type": "span", + "value": "\" at Sepolia block number " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "1391140 " + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "pragma solidity ^0.8.0;\n\ncontract Greeter {\n string private greeting;\n\n constructor(string memory _greeting) {\n greeting = _greeting;\n }\n\n function setGreeting(string memory _greeting) public {\n greeting = _greeting;\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now, lets create a proof of its storage slot 0 (the string variable " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "greeting" + }, + { + "type": "span", + "value": " ):" + } + ] + }, + { + "code": "const { bufferToHex } = require(\"ethereumjs-util\");\nconst { SecureTrie } = require(\"merkle-patricia-tree\");\nconst Web3 = require(\"web3\");\n\nconst web3 = new Web3(\"http://127.0.0.1:8545\");\n\nconst proveContractStorage = async (blockNumber) => {\n const proof = await web3.eth.getProof(\n \"0xf492600aed292b1b94a1ba0cd29fb6ed6d6ab872\", //the contract's address on Sepolia\n [0], //the first storage slot\n blockNumber\n );\n console.log(proof.storageProof);\n const value = web3.utils.hexToAscii(proof.storageProof[0].value);\n console.log(value);\n\n const proofBufs = proof.storageProof[0].proof.map(toBuffer);\n const proofTrie = await SecureTrie.fromProof(proofBufs);\n const valid = bufferToHex(proofTrie.root) == proof.storageHash;\n console.log(valid);\n};", + "type": "code" + }, + { + "code": "[\n {\n key: '0x0',\n value: '0x48656c6c6f2c204861726468617421000000000000000000000000000000001e',\n proof: [\n '0xf8518080a0e2a22c03c4f21673563d55cbad16de4c4affc9a54c3eea063ce358ccd6d02c4c8080808080808080a0fc47ec7aea920817d850c758a8fd61ecc89b967b2fe8d9ec6d00feedeb0a7d658080808080',\n '0xf843a0390decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563a1a048656c6c6f2c204861726468617421000000000000000000000000000000001e'\n ]\n }\n]\nHello, Hardhat!\ntrue", + "type": "code" + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "A non trivial example" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Creating proofs over nested structures, dynamic types like strings, or deeply buried storage slots is slightly harder because you have to be familiar with Solidity's " + }, + { + "url": "https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "storage layout" + } + ] + }, + { + "type": "span", + "value": ". To demonstrate that, here is a non-trivial example to prove that Jimmy Fallon owned " + }, + { + "url": "https://opensea.io/assets/ethereum/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d/599", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Bored Ape #599" + } + ] + }, + { + "type": "span", + "value": " at block height " + }, + { + "url": "https://etherscan.io/block/13572667", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "13572667" + } + ] + }, + { + "type": "span", + "value": " as he claimed during his Tonight Show " + }, + { + "url": "https://www.youtube.com/watch?v=5zi12wrh5So&t=222s", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "in January 22" + } + ] + }, + { + "type": "span", + "value": ". The Bored Apes NFT contract inherits from OpenZeppelin's legacy V3 " + }, + { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.2/contracts/token/ERC721/ERC721.sol#L36", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "enumerable ERC721 contract" + } + ] + }, + { + "type": "span", + "value": ". The data structure storing ownership information about a single token is the private " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "_tokenOwners" + }, + { + "type": "span", + "value": " member struct that maps a token id to an index onto another dynamic array of key value mappings." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove the ownership storage values we need to take a look at the contract's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "ownerOf" + }, + { + "type": "span", + "value": " implementation and its related structs. Here's a summary of the relevant parts of the " + }, + { + "url": "https://etherscan.deth.net/address/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d#code", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "full BAYC code base" + } + ] + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "library EnumerableMap {\n struct MapEntry {\n bytes32 _key;\n bytes32 _value;\n }\n\n struct Map {\n MapEntry[] _entries;\n // Position of the entry defined by a key in the `entries` array, plus 1\n // because index 0 means a key is not in the map.\n mapping (bytes32 => uint256) _indexes;\n }\n\n struct UintToAddressMap {\n Map _inner;\n }\n\n function _get(Map storage map, bytes32 key) private view returns (bytes32) {\n uint256 keyIndex = map._indexes[key];\n require(keyIndex != 0, \"EnumerableMap: nonexistent key\"); // Equivalent to contains(map, key)\n return map._entries[keyIndex - 1]._value; // All indexes are 1-based\n }\n\n function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {\n return address(uint160(uint256(_get(map._inner, bytes32(key)))));\n }\n //...\n}\n\ncontract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {\n\n // Mapping from holder address to their (enumerable) set of owned tokens\n mapping (address => EnumerableSet.UintSet) private _holderTokens;\n\n // Enumerable mapping from token ids to their owners\n EnumerableMap.UintToAddressMap private _tokenOwners;\n\n function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n return _tokenOwners.get(tokenId, \"ERC721: owner query for nonexistent token\");\n }\n //...\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The index mapping " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "_tokenOwners" + }, + { + "type": "span", + "value": " is the contract's third storage member (the 1st one being part of the ERC-165 implementation) and it implicitly points to the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "Map" + }, + { + "type": "span", + "value": " struct via " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "UintToAddressMap.inner " + }, + { + "type": "span", + "value": ", occupying two storage slots. By applying Solidity's " + }, + { + "url": "https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "layout rules for dynamic arrays" + } + ] + }, + { + "type": "span", + "value": ", the storage slot's address of the enumeration index that points to the current owner of token #599 can be computed as " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "const indexSlot = web3.utils.soliditySha3(599, 3);" + }, + { + "type": "span", + "value": ". Querying that storage slot's value by " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "web3.eth.getStorageAt(baycAddress, indexSlot, blockNumber)" + }, + { + "type": "span", + "value": " yields the index " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "0x258" + }, + { + "type": "span", + "value": " at the given block height. Considering that the " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "Map._entries" + }, + { + "type": "span", + "value": " array starts at the contract's third slot, it takes two slots to store one " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "MapEntry" + }, + { + "type": "span", + "value": " and the indexes are 1-based we can resolve the map's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "_value" + }, + { + "type": "span", + "value": " member that carries the token owner's address like so: " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "code" + ], + "value": "soliditySha3(2) + (0x258 * 2) - 2 + 1" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With that we can construct two storage proofs for the index and the owner's address:" + } + ] + }, + { + "code": "const baycAddress = \"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d\";\nconst blockNumber = 13572667;\n\nconst indexSlot = web3.utils.soliditySha3(599, 3);\n// -> 0x7e2616eb7a75f68a32624f502cf2cabc166c302900bbdc790c2fb85cea316a21\nconst indexValue = await web3.eth.getStorageAt(\n baycAddress,\n indexSlot,\n blockNumber\n); //0x258\n\nconst bnIndex = ethers.BigNumber.from(indexValue);\nconst valueSlot = ethers.BigNumber.from(web3.utils.soliditySha3(2))\n .add(bnIndex.mul(2))\n .sub(2)\n .add(1);\n// -> 0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5f7d\n\nconst proof = await web3.eth.getProof(\n baycAddress,\n [indexSlot, valueSlot],\n blockNumber\n);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove the " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " of the yielded proof, we can:" + } + ] + }, + { + "code": "async function validateStorage(proof, slot, proofIdx) {\n const proofBufs = proof.storageProof[proofIdx].proof.map(toBuffer);\n const pTrie = await SecureTrie.fromProof(proofBufs);\n const valid = pTrie.checkRoot(toBuffer(proof.storageHash));\n const rlpNode = await pTrie.get(toBuffer(web3.utils.keccak256(slot)));\n console.log(\"content at slot\", slot, bufferToHex(rlp.decode(rlpNode)));\n return valid;\n}\n\nconsole.log(await validateStorage(proof, indexSlot, 0));\nconsole.log(await validateStorage(proof, valueSlot.toHexString(), 1));", + "type": "code" + }, + { + "code": "content at slot 0x9f82913e56c1ea296cd5a3c46bc89a4073098f41767359e4c3742445923985c7 0x0258\ntrue\ncontent at slot 0xd4790f3899b463e8194660196b97cf3ff9c47008d83ca7c0e51ce406d3c784e5 \\\n0x0394451c1238cec1e825229e692aa9e428c107d8 (<- Jimmy Fallon's address)\ntrue", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Validating proofs inside smart contracts" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These have been client side examples, but proofs can also be validated inside Solidity contracts, e.g. to prove historical chain information that's not available to the contract itself. A good example can be seen at Lido Finance's " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "trustless ETH/stETH price pool oracles" + } + ] + }, + { + "type": "span", + "value": " that receives price reports as a combination of block header, account and state proofs. Since proofs are submitted " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle/blob/main/contracts/StableSwapStateOracle.sol#L295", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "as memory variables" + } + ] + }, + { + "type": "span", + "value": ", price updates " + }, + { + "url": "https://etherscan.io/tx/0xb24e20813e08f75e12e29da53f7f6c7e5dca7be68f3d3247edd4de572c527df4", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "are relatively cheap" + } + ] + }, + { + "type": "span", + "value": ". Here's a contract that's built on " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle/tree/main/contracts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Lido's primitives" + } + ] + }, + { + "type": "span", + "value": " and verifies any given state proof. It's also " + }, + { + "url": "https://goerli.etherscan.io/address/0x52e357f616a13089435be73e20cffb788eb4c928#code", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "deployed on Görli" + } + ] + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\n\nimport \"./StateProofVerifier.sol\";\nimport {RLPReader} from \"solidity-rlp/contracts/RLPReader.sol\";\n\ncontract ProofVerifier {\n using RLPReader for bytes;\n using RLPReader for RLPReader.RLPItem;\n using StateProofVerifier for StateProofVerifier.Account;\n\n /*\n struct Account {\n bool exists;\n uint256 nonce;\n uint256 balance;\n bytes32 storageRoot;\n bytes32 codeHash;\n }\n\n struct SlotValue {\n bool exists;\n uint256 value;\n }\n */\n\n function extractAccountFromProof(\n address _address,\n bytes32 _stateRootHash,\n bytes memory _proofRlpBytes\n ) public pure returns (StateProofVerifier.Account memory account) {\n RLPReader.RLPItem[] memory proofs = _proofRlpBytes.toRlpItem().toList();\n bytes32 addressHash = keccak256(abi.encodePacked(_address));\n\n account = StateProofVerifier.extractAccountFromProof(\n addressHash,\n _stateRootHash,\n proofs\n );\n }\n\n function extractSlotValueFromProof(\n bytes32 _slotHash,\n bytes32 _storageRootHash,\n bytes memory _proofRlpBytes\n ) public pure returns (StateProofVerifier.SlotValue memory slotValue) {\n RLPReader.RLPItem[] memory proofs = _proofRlpBytes.toRlpItem().toList();\n slotValue = StateProofVerifier.extractSlotValueFromProof(\n _slotHash,\n _storageRootHash,\n proofs\n );\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Continuing with the proof of Jimmy Fallon's Bored Ape, this is how you would prepare RLP encoded versions of the proof's node array as required by the contract's method interface:" + } + ] + }, + { + "code": "//converts an array of rlp encoded proofs to an rlp encoded array of proofs.\nconst rlpEncodeProof = (proof) => {\n const rlpDecodedProofs = proof.map((p) => rlp.decode(toBuffer(p)));\n return rlp.encode(rlpDecodedProofs);\n};\n\nconst rlpAccountProof = rlpEncodeProof(proof.accountProof);\nconst indexStorageProof = rlpEncodeProof(proof.storageProof[0].proof);\nconst valueStorageProof = rlpEncodeProof(proof.storageProof[1].proof);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and submit it to the contract:" + } + ] + }, + { + "code": "//state root of mainnet block #13572667\nconst blockStateRoot =\n \"0xa710dad6c716e0b762a671865cbe0d286f158198580f4ac97c4ace95ea85ba1b\";\nconst baycAddress = \"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d\";\n\nasync function main() {\n const Verifier = await ethers.getContractFactory(\"ProofVerifier\");\n // 0x52E357F616a13089435bE73E20CFfB788Eb4C928 on Görli:\n const verifier = Verifier.attach(process.env.CONTRACT_VERIFIER);\n\n //\"account\" is the bored apes contract\n const accountResult = await verifier.extractAccountFromProof(\n baycAddress,\n blockStateRoot,\n rlpAccountProof\n );\n const indexResult = await verifier.extractSlotValueFromProof(\n ethers.utils.keccak256(indexSlot),\n accountResult.storageRoot,\n rlpIndexProof\n );\n const valueResult = await verifier.extractSlotValueFromProof(\n ethers.utils.keccak256(valueSlot),\n accountResult.storageRoot,\n rlpValueProof\n );\n\n console.log(\n accountResult,\n indexResult.value.toHexString(),\n valueResult.value.toHexString()\n );\n}", + "type": "code" + }, + { + "code": "[\n exists: true,\n nonce: BigNumber { value: \"1\" },\n balance: BigNumber { value: \"0\" },\n storageRoot: '0x3f99b7df7989c11417c18b517c333ec74104e23ae76a50d578292ba3d466d77d',\n codeHash: '0x0ba5e25e74d81bab327110c8d8b44320f50ad5c3e91a546a5c5a9b605cf653b3'\n]\n0x0258\n0x0394451c1238cec1e825229e692aa9e428c107d8 // <- Jimmy Fallon, again.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Creating " + }, + { + "url": "https://eips.ethereum.org/EIPS/eip-1186", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "EIP-1186" + } + ] + }, + { + "type": "span", + "value": " compatible historical cryptographic proofs as demonstrated is a rather demanding task. It requires provers to traverse their archive nodes' LevelDBs, requiring up to 16 disk operations per account and storage slot. However, since state proofs like Jimmy Fallon's Bored Ape ownership at block height 13572667 are deterministic and valid forever, one could presciently collect all of the hashes along the nodes from root of the state tree down to specific nodes and index them. Chain indexers or RPC relayers like Metamask could use those hashes to execute proofs, thus minimizing trust in the service itself: each reply would be provable by the client." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network greatly simplifies the process of generating proofs for blockchain data, saving time for developers, and avoiding the extensive recursive querying of full-node databases that would otherwise be required." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "55510965", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "What Is a Proof and Why Do You Need One?", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "What Is a Proof and Why Do You Need One?" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "What Is a Proof and Why Do You Need One?" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1663659141-laconic_proof_blog_2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1663659141-laconic_proof_blog_2.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-09-20T08:01:23Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "rick-dudley-on-interchain-fm", + "title": "Rick Dudley Discusses Laconic Network on Interchain.fm", + "date": "2022-09-13", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1663008114-chjango.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick Dudley, the co-founder of " + }, + { + "url": "https://laconic.com", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the Laconic Network" + } + ] + }, + { + "type": "span", + "value": ", talks to Chjango Unchained on a special live-streamed episode of " + }, + { + "url": "https://www.youtube.com/watch?v=viE0BZGx_DU", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Interchain.FM" + } + ] + }, + { + "type": "span", + "value": " about the risks of monopolization, why DApps have a hard time querying Ethereum data, how the Laconic Network solves this problem, and the legal aspects of creating a truly decentralized DApp data marketplace." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "How Laconic preserves verifiability" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is a data indexing service with an Ethereum L2 rollup that uses the Cosmos SDK and a fork of Ethermint. “Primarily, what we're doing is making third-party verifiable caches and indexes of Ethereum data, and we're building a marketplace to facilitate the buying and selling of that data.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For Ethereum-based apps, developers only need a subset of the Ethereum state to run their DApp. Laconic connects you with service providers who can serve that data to DApps, all in a decentralized, disintermediated way." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike other DApp data providers, Laconic focuses on the verifiability of the provided data. Rick points out that “there is no other platform that is preserving the verifiability in the way that we are.” Achieving this verifiability is anything but easy. If a DApp wants to run verifications using an archive node, it would take months of moderate computing time to do that, not to mention archival storage. “Most people don't have 14 terabytes just laying around that they can attach to their phone or laptop.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s answer is to split the data and decentralize validation. As Rick explains, “we take the whole Ethereum blockchain. And then we get a bunch of validators together and make you a little mini proof-of-stake blockchain of just the information you care about.” Ethereum remains the L1 in this scenario; “all the value that's transacted on the Laconic Network is actually staked or escrowed on Ethereum.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Leveraging linked data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic solution builds upon an established data format (IPLD, or Interplanetary Linked Data) to deliver the promise of verifiable data. The IPLD format not only allows transforming data to better serve the needs of DApps, but also makes it possible to verify these transformations. “It's not magic, but it makes it easier for us to deal with these very complex and robust data types. It also allows us to do cross-chain proofs, which is the main appeal.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only cross-chain proofs, but proofs about Ethereum events—which is not possible in Ethereum directly. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "“Events were never intended to be provable there. They weren't supposed to be used in the way they're used today.”  Rick explains how difficult it is for the average DApp developer to know that a single event really came from Ethereum, “You have to have the hundreds or thousands of events that came with that event, or you have to rerun the transaction in the block and see that it emitted the event. And both of those things are extremely expensive.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic has a technique for saving the effort of doing that re-computation, and IPLD plays a large part in that." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Acting lawfully" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network will have up to 67 validators that will be located in different countries. This raises some legal questions. Chjango points to a scenario where sanctions against countries can break the promise of decentralization. Rick explains how Laconic’s legal structure addresses this issue. Laconic is run by an LLC that is designed to “comply with the laws within its jurisdiction and to allow each member to comply with the laws within their jurisdiction.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If a country imposes sanctions on another country, the members are free to follow their local law and refuse service to members of the sanctioned countries. At the same time, the rest of the Laconic Network remains available for those accounts. All this can be done without sacrificing privacy. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "“You can use encryption and other things for someone to say ‘is this user in the United States, yes or no,’ without revealing where the user actually is.” Rick concludes that, “of the 67 members, we hope to have members that are willing to and legally able to service customers in those jurisdictions that are currently underserved.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Blockchain decentralization, or the lack thereof" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization in conjunction with global distribution is key to providing uninterrupted service in the face of ill-intended governments or political disturbance. Rick considers decentralization to be retreating, giving way to new monopolies. In other words, there is a re-centralizing effect." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The problem starts with the cost of verification. Chjango asserts that “the difficulty of fully verifying an Ethereum blockchain is such that it has hurt its decentralization, even though the underlying blockchain is decentralized.” The expense of running a fully validating node has introduced new centralization choke points." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick places the problem one level down, at the network. The internet comprises 60,000 sub-networks that communicate over the Border Gate Protocol (BCP). But a traffic analysis found Ethereum network traffic on only 18 subnets. And a handful of companies – AWS, Google, Microsoft, Cloudflare –  run much of the infrastructure, and are also in the same legal jurisdiction. So a single company can switch off large parts of the Ethereum system." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This lack of decentralization continues at the blockchain level. “There are 4000 validating nodes on the network, but about 80% of them are in AWS. And somehow, in spite of that, 50% of the blocks still come from three people.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic's answer to this problem is “forced decentralization.” Rick stated, ”Laconic requires its members to run their own hardware and to be located in different data centers. And we may at some point require people to run their own networks on the internet, so that we do maintain decentralization at that level.” Here, the legal side plays an important role.  The LLC can positively assert that a Member is a specific legally registered entity. They signed a contract that stipulates they avoid being in the same data center as another Member, or else they're in breach of the contract and they risk being removed from the network. \"That's a big benefit of the LLC, and definitely something that we've spent time thinking about to design it right,\" Rick continued." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Monopolistic forces are unavoidable. Give people an option to exit a toxic monopoly" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Even with Laconic’s solution, scalability and sustainability of the whole blockchain ecosystem are not a given. As pointed out earlier, more than 50% of Ethereum blocks are created by only three mining pools." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "(Source: " + }, + { + "url": "https://etherscan.io/stat/miner?blocktype=blocks", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Etherscan.io" + } + ] + }, + { + "type": "span", + "value": ")" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Such monopolization tendencies are hard to fight. Rick’s view is to initially accept the inevitable. “I think it's more about acknowledging that these things are going to be monopolized. And then building technology that allows us to exit those monopolies as needed, more than trying to build technology that's going to resist the monopoly.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The aim of Laconic is to make it trivial for people to exit when those systems get monopolized. To achieve this, Laconic has a few layers of protection in place to stop the Laconic Network from being compromised by monopolization attempts. As an example, the legal agreements prevent Laconic members from sharing investors. So an investor cannot simply invest in over one-third of the members to take it over." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But if some sort of monopolization does occur, every DApp user has an option to exit. “If every user of a DApp has their own Watcher (Laconic’s API servers) running, they would have all the state they needed to move that DApp to another chain. They could take that one Watcher, generate a new state snapshot for that application, and then move that state snapshot to a new chain. And that literally would take a year of processing on Ethereum." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What’s in the name Laconic?" + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Why “Laconic”? " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick explains, “It means terse.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Referencing the ancient inhabitants of Laconia known for their more concise and terse style of speech, Laconic also describes a style of speaking or writing that uses only a few words, often to express complex thoughts and ideas." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s “laconic” technology  elegantly simplifies the complexity of blockchain, and accelerates DApp development." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Takeaways" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This was a wide-ranging interview with deep-dives on many topics not covered here. Dive in to find out more about:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Payment channels and validator accountability." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Processing time and why they don’t discuss network speed or TPS at Laconic." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Why Laconic chose Geth over other Ethereum clients." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Catch the full interview on " + }, + { + "url": "https://www.youtube.com/watch?v=viE0BZGx_DU", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "YouTube" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thanks to " + }, + { + "url": "https://www.youtube.com/channel/UCMcYl850ni93ZDqchYF7v1w", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Chango Unchained" + } + ] + }, + { + "type": "span", + "value": " and Interchain.FM for leading this thought-provoking interview with Rick. It’s exciting to be able to showcase the work happening at Laconic, designing and implementing a solution for truly decentralized delivery of provable data from the Ethereum blockchain." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "55457818", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Rick Dudley Discusses Laconic Network on Interchain.fm", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Rick Dudley Discusses Laconic Network on Interchain.fm" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Rick Dudley Discusses Laconic Network on Interchain.fm" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1663008114-chjango.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1663008114-chjango.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-09-13T15:05:48Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "laconic-watchers", + "title": "Laconic Watchers: Ensuring Trustlessness in Web3", + "date": "2022-09-06", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "24856058", + "name": "Maly Ly" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1662426120-laconic-watchers.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Watchers in the Laconic Network are the keystone technology that will scale blockchain data access for the next wave of Web3 adoption. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic stack provides a way for DApp developers to retrieve the blockchain data they require in a manner that is efficient, verifiable, and decentralized. The mechanism by which the relevant subset of blockchain data is queried, cached, and delivered, is the Laconic Watcher." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve previously explored " + }, + { + "url": "https://www.laconic.com/blog/how-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "how the Laconic Network uses IPLD" + } + ] + }, + { + "type": "span", + "value": " (“the data model of the content-addressable web”) to keep off-chain data cryptographically verifiable across transformations. The Laconic Watcher is the component that makes that data available for DApps.  You will learn about the role of Watchers within the Laconic Network, how they work, and what you can use a Watcher for." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "DApp Developer Challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A public, permissionless blockchain is optimized for storing large amounts of data and achieving consensus. A DApp typically needs only a tiny fraction of that data for its operations. Ethereum clients are not designed to serve the data needs of a given DApp, such as reading and linking data from multiple contracts, introducing cross-chain data, or presenting DApp-specific information to users. Existing options for retrieving blockchain data have some drawbacks:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Significant effort required: " + }, + { + "type": "span", + "value": "Running an archive node gives access to all of a blockchain’s data.  Structurally, blockchains are write rather than read optimized. Many developers are unable to, unwilling to, or cost-prohibited from, running the infrastructure." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Trustlessness broken: " + }, + { + "type": "span", + "value": "Centralized services can provide indexed blockchain data, but  they lack the mechanisms to cryptographically prove their accuracy and provenance. DApp developers and users must trust these services, contrary to the decentralized and trustless promises of Web3." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our goal is to make DApp development as frictionless as possible. When you build your DApps, Laconic Watchers relieve you of the infrastructure burden and enable you to query and receive trustless, verifiable, off-chain blockchain data." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Watcher and the Laconic Network" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To understand the Watcher’s role and functionality, let's briefly recap how the Laconic solution works." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The primary function of the Laconic Network indexing is to keep an up-to-date version of blockchain data in a relational database. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To achieve this, a Laconic Full Index Node fetches the latest blockchain updates and turns them into IPLD blocks with advanced indexing. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike existing ETL (Extract-Transform-Load) solutions that convert blockchain data into a searchable database format, the Laconic Network continuously monitors and evaluates state diffs on the chain, retrieving and indexing relevant tries." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This additional data opens up a new range of opportunities for DApps. This is the first component of the Laconic solution.  However, the database is still too large and too general for DApps to use; raw blockchain data structures are not suitable for DApps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Enter the Laconic Watcher." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Your DApp’s Personal Data Delivery Service" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Watchers are the component that makes DApp development as frictionless as possible. Watchers serve three fundamental purposes:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Query" + }, + { + "type": "span", + "value": " the Laconic Full Index Node for the specific data your DApp needs." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Transform" + }, + { + "type": "span", + "value": " the queried data to make it consumable for your DApp." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cache" + }, + { + "type": "span", + "value": " the data for fast and inexpensive access." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Watchers run queries relevant to your DApp through a GraphQL interface; you can create precisely the API you need, transforming the data as required. Watchers run as daemons, constantly updating the cache with transactions from the blockchain's head." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your Watcher is effectively your personal, virtual, read-only blockchain, containing the specific data your DApp needs. You get the same verifiability you would from the source blockchain, without the same burden or overhead." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Where do I find Watchers? How can I write one? Do I have to?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Running Watchers: " + }, + { + "type": "span", + "value": "It is the job of Service Providers on the Laconic Network to run Watchers. One or more Service Providers can run a given Watcher." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Do I have to write my own Watcher?" + }, + { + "type": "span", + "value": " As a DApp developer, you don’t necessarily need to create your own Watcher. Some alternatives:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Pay to access a public Watcher API. " + }, + { + "type": "span", + "value": "The fees are split between the Service Provider and the Watcher creator." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Check the Watcher Registry:" + }, + { + "type": "span", + "value": " If a Watcher’s creator chooses to make it publically available, they can add the Watcher to the on-chain Watcher Registry for easy discovery. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "You don’t have to start from scratch. " + }, + { + "type": "span", + "value": "Watchers are composable. You can build a Watcher on top of one or more existing Watchers found in the Watcher Registry." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What about Smart Contracts? " + }, + { + "type": "span", + "value": "As a Smart Contract author, you can auto-generate Watcher code directly from your Smart Contract's Solidity source code. The code generator is capable of generating the Watcher GraphQL API based on eth_calls as well as storage variables in the Smart Contract." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Watchers, in Conclusion" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Watchers" + }, + { + "type": "span", + "value": " allow DApps to cache and query verifiable blockchain data served by a decentralized network of Service Providers. Watchers facilitate the creation of custom APIs suitable for a DApp’s specific needs. " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "As a developer," + }, + { + "type": "span", + "value": " you will benefit from the Laconic Network by finding, writing, or combining Watchers to serve the backend data needs of your DApps." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "As a Laconic Network Service Provider or Validator," + }, + { + "type": "span", + "value": " you will be part of solving the problems of blockchain data access for the next wave of Web3 adoption. " + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stay up-to-date with Laconic News" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Join our " + }, + { + "url": "https://discord.com/invite/ukhbBemyxY", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Discord server" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Subscribe to our " + }, + { + "url": "https://t.me/share/url?url=https%3A%2F%2Fwww.laconic.com%2Fblog%2Fhow-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Telegram channel" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Sign up for our mailing list below" + } + ] + } + ] + } + } + }, + "featured": true, + "id": "55448692", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Laconic Watchers: Ensuring Trustlessness in Web3", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Laconic Watchers: Ensuring Trustlessness in Web3" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Laconic Watchers: Ensuring Trustlessness in Web3" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1662426120-laconic-watchers.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1662426120-laconic-watchers.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-09-06T06:16:55Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "how-laconic-network-uses-ipld", + "title": "How Laconic Network Uses IPLD", + "date": "2022-08-30", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "7023106", + "name": "Christoph Berger" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1661833374-ipld-blog.png" + }, + "content": { + "blocks": [ + { + "id": "55419640", + "title": "IPLD" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Accessing blockchain data off-chain is no longer a matter of trust" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The mission of the Laconic Network is to ensure that decentralized off-chain caches can serve blockchain data without losing the ability to prove the integrity of that data across data transformations. This post shows how the Laconic Stack maintains the verifiability of Ethereum data for its decentralized caching and querying solution. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Querying blockchain data is difficult and expensive" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "DApp developers face a dilemma when it comes to working with blockchain data. Ethereum data is stored in various ways, including the state, storage, and transaction tries, as well as in event logs. The original concept for accessing Ethereum data was that DApps would run or have access to an Ethereum full node and query it directly. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "However, DApp developers rarely want to run their own full node only to gain access to the data they need. Further, and more problematic, due to the way data is stored on Ethereum, there is often no direct way of querying for particular information. Because blockchains are optimized for writing new blocks and achieving consensus, they cannot be indexed like a classic database. As a result, to query for data, developers have to make multiple calls to the Ethereum API, replay transactions to read the events emitted, and store intermittent state in the application to join it with data from subsequent invocations.  This process is difficult and expensive." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The alternative to running a full node is to use a middleware indexing and caching service. While this alternative simplifies data acquisition for developers, it introduces some new and significant problems. In addition to high costs, long setup times, reliability issues, and the dependence on Web2-style centralized services, there is one problem that is especially pernicious: the data served by these services must be " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "trusted" + }, + { + "type": "span", + "value": ". It is currently impossible for existing centralized data providers to offer cryptographic proofs for every byte of data served." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Solution: Laconic Network and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Many people are familiar with the Interplanetary File System (IPFS), but fewer people know about the Interplanetary Linked Data (IPLD) protocol. IPLD is the underlying technology of IPFS, and it is the superpower technology behind many of IPFS’s advantages. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD can represent arbitrary data in self-describing structured objects, and it can link those objects into Merkle DAGs. As such, IPLD offers the following advantages:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is content-addressable via a Content ID (CID), just as IPFS files are content-addressable." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is cryptographically provable, meaning you can verify that a set of data belongs in a larger data set even without needing to have either set on hand. You only need the cryptographic hashes." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data undergoes natural deduplication, reducing storage and transport costs." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is tamper-proof. Any attempts to change the data that you have requested via content addressing can be foiled by validating the hashes." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD codecs map data from its original form to the IPLD data model. So if a codec exists for that data, the data has a formal representation in the IPLD data model. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Connection to Ethereum" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the case of Ethereum data, the " + }, + { + "url": "https://ipld.io/specs/codecs/dag-eth/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "DAG-ETH codecs" + } + ] + }, + { + "type": "span", + "value": " can be used to map on-chain data to off-chain IPLD structures. This mapping to IPLD makes it possible to inspect, process, or reason about data in a uniform way. The codecs cover block headers, uncles, transactions, transaction receipts, and receipt logs, as well as all the different Merkle Patricia tries that are rooted in an Ethereum block. As a result, IPLD can accurately represent any Ethereum data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike the original data, the off-chain IPLD representation enables indexing for fast querying. The restrictions of the blockchain do not apply here. Indexes can be added that allow developers to query data in ways that are otherwise difficult to achieve with the native ETH JSON RPC API. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "More importantly, the IPLD representation of blockchain data can be kept verifiable even after transforming the data into other models. Transformations are necessary to support complex DApps that aggregate and link data in app-specific ways or rely on a non-native representation of the data. The classic methods of slicing, dicing, and recombining the data would render it unverifiable. With Laconic Network data, thanks to our use of IPLD, the data remains as verifiable as it is on the blockchain." + } + ] + }, + { + "item": "55419640", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Merkleized data is provable data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The IPLD representation of an Ethereum block includes the block header and the uncles, transactions, receipts, logs, receipt trie, transaction trie, state trie, and storage tries that Ethereum stores alongside the block header. A modified Merkle Patricia Trie (MMPT), which is relevant to Ethereum, has branch nodes, extension nodes, and leaf nodes. Non-leaf nodes store the content hashes of their child nodes. This hashing goes all the way up to the root node. Therefore, the hash value in the tree root represents all leaf data. If any part of the data in a Merkle tree is tampered with, the root hash would change and differ from the root hash stored in the block header. In other words, data like state, transactions, and more, is connected to the block header in a provable way.  " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Storing all of the IPLD: Laconic Full Index Nodes" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Traditionally, Ethereum Full Nodes are used to power DApps that need to access and query all of the data of the blockchain. The disadvantages of relying on a normal Full Node are well known: " + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Expensive to operate" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not optimized for data querying" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Logs stored from events have to be replayed to access the data" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Full Index Nodes (LFIN) overcome these limitations by generating additional derivative data based on that which is stored in the Ethereum Full Node, adding new indexes, relational mappings, and materialized views, and even merkleizing some data that was previously not merkleized. The result is an amount of data that far exceeds what is normally stored. The role of the LFIN is to track and store the complete data, including derived indexes and Merkle trees of that data, in IPLD block format, for eventual consumption by DApps. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data representation inside of the LFIN optimizes many classes of queries by removing the need  to chain together data from multiple transactions. For example, listing all transactions for a particular wallet address could be achieved with a single query. Or gathering all of the node hashes needed to generate a cryptographic proof. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Laconic Watchers - interoperable and provable blockchain data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because the volume of data in the Laconic Full Index Node is so vast, most DApps would not want to have to query that database directly. Instead, DApps work with a specialized caching layer that exists to transform LFIN data into the format needed by specific DApps. These are Laconic Watchers. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data in the LFIN is still not ready for DApp developers at this point. Further transformation is needed to structure the data in the format required by your DApp. This is the role of the Laconic Watcher. Watchers will be the topic of a subsequent article, but within the scope of this article, you can understand them as being custom secondary or tertiary caches of data that directly fulfill the needs of specific applications. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thanks to our use of IPLD, these transformations can occur without losing the cryptographic integrity of the data. Developers can build their Watchers to track, process, and cache the underlying blockchain data, and then query the watchers using GraphQL. The returned data includes the linked hash structure that can be followed all the way back to the underlying blockchain. In the Laconic Network, when any data is being transformed, the hash of the inputs to the transformation and the hash of the code that performs the transformation are preserved. Thus:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "code" + ], + "value": "T(a+b) => c" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The new model, c, will contain content-hash references to a, b, and the code performing T." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One intentional consequence of our adoption of structured IPLD models for data representation is that it greatly simplifies dealing with cross chain data. Our ability to transform data while maintaining linked hashing will be invaluable when we begin to index chains beyond Ethereum. Laconic Network is designed with blockchain interoperability in mind." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The source of proof" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network improves the Web3/blockchain ecosystem greatly by combining the validation-preserving nature of IPLD with a new way of caching and transforming blockchain data in a decentralized manner. This ability frees DApp developers from relying on centralized, trust-based data providers and closes the cryptographic provability gap between the blockchain and DApps built to use blockchain data. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Stay up-to-date with Laconic news:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://discord.com/invite/ukhbBemyxY", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Join our Discord server" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://t.me/share/url?url=https%3A%2F%2Fwww.laconic.com%2Fblog%2Fhow-laconic-network-uses-ipld", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Subscribe to our Telegram channel" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Sign up for our mailing list below" + } + ] + } + ] + } + ] + } + ] + } + } + }, + "featured": true, + "id": "43258780", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "How Laconic Network Uses IPLD", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "How Laconic Network Uses IPLD" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "How Laconic Network Uses IPLD" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1661833374-ipld-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1661833374-ipld-blog.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-08-31T00:13:42Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + }, + { + "slug": "introducing-laconic-network", + "title": "Introducing Laconic Network", + "date": "2022-07-26", + "category": [ + { + "id": "2965426", + "slug": "news", + "title": "News" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "24856058", + "name": "Maly Ly" + }, + "image": { + "url": "https://www.datocms-assets.com/66113/1659030103-introducing-laconic.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The advent of smart-contract blockchains, led by Ethereum in 2014, has given rise to exciting new possibilities for humans interacting and transacting with each other. The ensuing rise of Web3, with wider consumer adoption of DeFi and NFTs in particular, has begun to show the world how public blockchains that are permissionless and trustless can bring immense value to industries and individuals. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The value of blockchain:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Immutable data that brings transparency to finance and governance" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Efficiency through smart contract automation " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Security and privacy through emerging cryptography " + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Yet all of these benefits hinge on developers succeeding in writing Web3 applications on top of blockchains that are fast, efficient, secure, and frictionless to use. There will be no internet-scale adoption of Web3 if the user experience is bad, or if it is simply too difficult to write the applications in the first place. And this point highlights a critical weakness of blockchains - accessing the data that they store is not as simple and straightforward as one might expect. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the contrary, since blockchains are write-optimized databases that focus nearly exclusively on establishing consensus and finality of each new block, they are notoriously difficult to work with for data retrieval." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The result of this has been the rise of a middleware industry to index, cache, and serve blockchain data. This includes services like Infura, Alchemy, and The Graph. These services, while more convenient for developers to use when building DApps, all break the fundamental benefits of blockchain in one or more ways. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, they tend towards centralization, and we end up with an industry where the blockchains are decentralized, but the DApps built on them all pass through an oligarchy of service providers that are not materially different from the IaaS cloud providers that represent the hyper-centralization of Web2. Second, they serve data that can no longer be cryptographically proven to be the correct data, so the trustless benefit of blockchain technology is replaced with a trust relationship with a single company. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Until Laconic, there has not been any technology that serves blockchain data while truly upholding the ideals of decentralization and allowing all data to be independently cryptographically verified. Key components of the emerging Web3 ecosystem, like Metamask, use centralized APIs from OpenSea and Infura without any mechanism for cryptographically verifying the accuracy or provenance of the data. Yet, provable data and decentralization are essential to having truly trustless systems for Web3 to depend on." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It is precisely this service that Laconic has been created to deliver." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Blockchain Data for High-Performance Applications" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network solves these two fundamental and existential problems. We are creating a truly decentralized network of data indexers and API providers to deliver hash-linked (cryptographically provable) blockchain data off-chain. Every byte of data served to DApps via the Laconic Network can be verified and proven to be mathematically integral." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We are thrilled at the prospect that the Laconic Network will become the catalyst and enabler to unlock immense potential value in the blockchain. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the last five years, our team of platform experts across Ethereum, IPLD / IPFS, and Cosmos SDK has been leading research and development in this space. We are creating a new set of technologies and infrastructure to solve the fundamental problem of making blockchain data accessible to high performance, high availability applications. The work has included the application of IPLD block structures to preserve the hash-linked structure of blockchain data when stored in traditional relational databases and served via API endpoints." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "The upshot: No longer will DApp developers have to struggle with convoluted queries, poor performance, long indexing times, or unprovable data when building decentralized applications. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Enter the Laconic Network" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Furthermore, we’ve pioneered a novel organizational and governance structure that solves the seemingly incompatible goals of providing guaranteed data availability service levels via a truly decentralized network. Laconic Network Members are corporate entities who enter contractual obligations to offer data indexing and retrieval services to end-users with professional-grade performance guarantees. Yet the network of these partners is technically, legally, and even geographically decentralized, and therefore impervious to censorship or monopolistic control. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the primary responsibilities of Laconic Network Members is to run Laconic Watchers, which are programs that expose data APIs to DApps, maintaining specific views of blockchain state and data expressed as mini-blockchains, solely for the purpose of reliable, provable data querying. DApp developers can find and use existing Laconic Watchers and can programmatically create new Watchers. There will be token incentives for programmers who write Watchers. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, the Laconic Network features an incentivized data caching layer to ensure robustness.  All blockchain data can be made permanently, globally available through the caching layer, without needing to directly query blockchain nodes, and, of course, without sacrificing the cryptographic integrity of the data. People who care about decentralization and provable data will be able to participate in this caching layer and earn tokens while doing it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The upcoming launch of the Laconic Network will bring a new era marked by the positive value that blockchains bring to humanity, with ever more practical and impactful use cases emerging as the barriers of data availability fall away." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network will have opportunities and benefits for anyone who cares about the future of the decentralized web:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://discord.com/invite/ukhbBemyxY", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Join our Discord" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://t.me/laconicnetwork", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Chat with us on Telegram" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and " + }, + { + "url": "https://www.laconic.com/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "sign up for our Newsletter" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "How have others tried to solve these challenges?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The original vision of Ethereum was that DApp developers would connect directly with Ethereum nodes and use the JSON-RPC API of an Ethereum client to query data. This allows you to get data from a source that has verified the data, and is decentralized, but it requires you to run the node yourself, or hire someone to run it for you. Furthermore, the data is not indexed in such a way that is convenient for DApp developers, and a large number of queries and processing must be done to satisfy normal application cases." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To improve the convenience of querying Ethereum data, many indexers have taken the data and put it in off-chain databases, often with sensible indexes. They then expose this data over new APIs that foster rapid application development and developer ease, but completely forsake the first principle values of decentralization and cryptographic verifiability. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To our knowledge, no indexer has managed to provide the convenience of custom indexes and APIs while preserving decentralization and cryptographic proofs of the data integrity." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Laconic's Solution: How are we different?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Developed over four years by our team of Ethereum, IPLD, and Cosmos SDK core contributors, the Laconic Solution encompasses three main components:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Stack, a set of services which convert Ethereum data into IPLD blocks and populate our high-performance, carefully indexed databases.\n\n" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic SDK, which makes it easy for developers to write front ends and other services which consume IPLD-based data structures and proofs.\n\n" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network, a multi-sided marketplace that allows DApp developers and service providers to transact in a more cost-effective way and then pass those cost savings on to end-users, providing a sustainable and decentralized operating model for DApp developers. The key element of the Network is Watchers, custom materialized views of blockchain data that developers query to access specific blockchain data." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Stack consists of our modified Geth client which extracts and denormalizes the data, transforming it into IPLD blocks and indexing it properly for efficient querying and future transformations. For example, we can list all the node hashes needed to execute a cryptographic proof in one query, instead of having to walk down the Merkle tree with a chain of sequential queries. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The main contact point for developers is the Watcher, a materialized view of a specific subset of blockchain data (e.g. a specific contract or protocol) that provides a custom endpoint for DApps to query.  " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "$LNT, the Laconic Network Token" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With a fixed total supply, Laconic Network Token (LNT) serves multiple functions in the network design, most saliently by securing incentive alignment across network stakeholders. In addition to being used as rewards for service providers to run Watchers, LNT also allows for seamless transactions across services, regardless of blockchain or DApp." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Distribute rewards to staked users" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Provide service discounts" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Distribute funds to community members for Network maintenance and development" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "What’s Next for Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth mode, we’re looking forward to our public marketing debut this month. Later in Q3 2022, we will announce the initial cohort of seven Founding Members, as well as a funding round. As our technology, governance, and network are novel, with no current analogies, we're excited to share more articles about key aspects of the Laconic Stack and Laconic Network, including our use of IPLD, the role of Watchers, the features of the Laconic App, and our pioneering governance framework." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For those wishing to run validators, we're aiming to have an incentivized Testnet in 2023, followed by the launch of our Mainnet later in 2023." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Summary of Laconic Network Milestones" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Public Marketing Launch: Q3 2022" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Coming out of a long stealth period, we’ll be launching our marketing assets and channels, including branding, website, social media and developer platforms, and content." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Seven Founding Members and Funding: Q3 2022" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ll be announcing our initial seven Founding Members and the close of our seed funding round." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentivized Testnet: Early 2023" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With the launch of our Incentivized Testnet, prospective validators will be incentivized to learn about the requirements for being a Member/Validator on Laconic Network and how to operate the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Mainnet Launch & Incentivized Global Data Cache: Late 2023" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Once Laconic Mainnet launches, " + }, + { + "type": "span", + "value": "and 7 Member/validators have joined the network, we’ll have an incentivized global data cache, allowing anyone to easily participate in serving blockchain data to DApps (and earn $LNT in doing so)." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "24195830", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Introducing Laconic Network", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Introducing Laconic Network" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Introducing Laconic Network" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "https://www.datocms-assets.com/66113/1659030103-introducing-laconic.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "https://www.datocms-assets.com/66113/1659030103-introducing-laconic.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "2022-08-30T04:19:58Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/careerPageAllPositionsQueryResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/careerPageAllPositionsQueryResult.json new file mode 100644 index 0000000..f01e3d7 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/careerPageAllPositionsQueryResult.json @@ -0,0 +1,5 @@ +{ + "data": { + "allPositions": [] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/communityPageAllEventsQueryResult - Copy.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/communityPageAllEventsQueryResult - Copy.json new file mode 100644 index 0000000..01b3ae1 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/communityPageAllEventsQueryResult - Copy.json @@ -0,0 +1,54 @@ +{ + "data": { + "allEvents": [ + { + "title": "Cosmoverse", + "eventDesc": "The legendary Cosmos Conference", + "eventStartdate": "2022-09-27", + "eventEnddate": "2022-09-28", + "eventLocation": "Medellin, Colombia", + "id": "14094472", + "image": { + "url": "/images/site_content/event/20220927-cosmoverse.png" + }, + "link": "https://www.cosmoverse.org/" + }, + { + "title": "Mainnet", + "eventDesc": "Mainnet is an immersive, agenda-setting summit held annually by Messari.", + "eventStartdate": "2022-09-21", + "eventEnddate": "2022-09-23", + "eventLocation": "New York, USA", + "id": "14069499", + "image": { + "url": "/images/site_content/event/20220921-mainnet.png" + }, + "link": "https://mainnet.events/" + }, + { + "title": "ETH SF", + "eventDesc": "ETHSanFrancisco 2022 will be the year’s premier Ethereum event on the west coast, so don’t miss your chance to see your SF friends' apartments one last time before they move to Miami.", + "eventStartdate": "2022-11-03", + "eventEnddate": "2022-11-05", + "eventLocation": "San Francisco, USA", + "id": "14094487", + "image": { + "url": "/images/site_content/event/20221103-eth-sf.png" + }, + "link": "https://sf.ethglobal.com/" + }, + { + "title": "Devcon", + "eventDesc": "Devcon is an intensive introduction for new Ethereum explorers, a global family reunion for those already a part of our ecosystem, and a source of energy and creativity for all.", + "eventStartdate": "2022-10-07", + "eventEnddate": "2022-10-14", + "eventLocation": " Bogota, Colombia", + "id": "14069491", + "image": { + "url": "/images/site_content/event/20221007-devcon.png" + }, + "link": "https://devcon.org/en/" + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/communityPageAllEventsQueryResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/communityPageAllEventsQueryResult.json new file mode 100644 index 0000000..b4b55b8 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/communityPageAllEventsQueryResult.json @@ -0,0 +1,54 @@ +{ + "data": { + "allEvents": [ + { + "eventDesc": "The legendary Cosmos Conference", + "eventEnddate": "2022-09-28", + "eventStartdate": "2022-09-27", + "eventLocation": "Medellin, Colombia", + "id": "14094472", + "image": { + "url": "https://www.datocms-assets.com/66113/1652360300-card03.png" + }, + "link": "https://www.cosmoverse.org/", + "title": "Cosmoverse" + }, + { + "eventDesc": "Mainnet is an immersive, agenda-setting summit held annually by Messari.", + "eventEnddate": "2022-09-23", + "eventStartdate": "2022-09-21", + "eventLocation": "New York, USA", + "id": "14069499", + "image": { + "url": "https://www.datocms-assets.com/66113/1652352588-card02.png" + }, + "link": "https://mainnet.events/", + "title": "Mainnet" + }, + { + "eventDesc": "ETHSanFrancisco 2022 will be the year’s premier Ethereum event on the west coast, so don’t miss your chance to see your SF friends' apartments one last time before they move to Miami.", + "eventEnddate": "2022-11-05", + "eventStartdate": "2022-11-03", + "eventLocation": "San Francisco, USA", + "id": "14094487", + "image": { + "url": "https://www.datocms-assets.com/66113/1652360353-card04.png" + }, + "link": "https://sf.ethglobal.com/", + "title": "ETH SF" + }, + { + "eventDesc": "Devcon is an intensive introduction for new Ethereum explorers, a global family reunion for those already a part of our ecosystem, and a source of energy and creativity for all.", + "eventEnddate": "2022-10-14", + "eventStartdate": "2022-10-07", + "eventLocation": " Bogota, Colombia", + "id": "14069491", + "image": { + "url": "https://www.datocms-assets.com/66113/1652352490-card01.png" + }, + "link": "https://devcon.org/en/", + "title": "Devcon" + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/homePageAllTestimonialsQueryResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/homePageAllTestimonialsQueryResult.json new file mode 100644 index 0000000..eb89099 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/homePageAllTestimonialsQueryResult.json @@ -0,0 +1,78 @@ +{ + "data": { + "allTestimonials": [ + { + "id": "53676939", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1660570305-t1qh-epl_400x400.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "Here’s a few of my favorite slept on protocols at the moment:\n\n@ComposableFin\n@laconicnetwork\n@archwayHQ\n@SeiNetwork\n@Shade_Protocol\n \nEach doing industry leading work in their respective niche and poised to gain significant market share during the bear market and beyond", + "testimonialUsername": "@DougieDeLuca" + }, + { + "id": "53682921", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1652432456-eeb-whtv_400x400.jpg" + }, + "testimonialPosition": "", + "testimonialText": "Running infura for free is actually extremely expensive, we pursued a light client before but it required a fresh implementation. hopefully @laconicnetwork solves this problem", + "testimonialUsername": "@kumavis" + }, + { + "id": "53682920", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1652722398-yddlfryh_400x400.jpg" + }, + "testimonialPosition": "", + "testimonialText": "If you love web3 infra layer tools/solutions, give @laconicnetwork a follow. I'm incredibly excited with what they have coming down the pipeline…\n", + "testimonialUsername": "@rickmanelius" + }, + { + "id": "53682912", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1660570564-f6qitfr_400x400.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "if you're also wondering how we level up (from ground-level projects), @laconicnetwork looks like a compelling middleware layer to me 👀", + "testimonialUsername": "@Ether_Gavin" + }, + { + "id": "53682908", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1660570523-sjtwlhch_400x400.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "shoutout to @cosmosibc and @nomadxyz_, and @agoric and @laconicnetwork", + "testimonialUsername": "@dystophiabreaker" + }, + { + "id": "53682905", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1660570491-spncmxjy_400x400.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "I feel hopeful for and inspired by what @laconicnetwork is building 🌟", + "testimonialUsername": "@cjremus" + }, + { + "id": "53682698", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1660570450-clc5-paa_400x400.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "excited about @laconicnetwork\n \nthis is what actual free market mutualism can create - incentive structures around maintaining tailored state data that can then be made valuable to others as a self-sustaining public good.", + "testimonialUsername": "@motherpredicate" + }, + { + "id": "53681948", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1660570379-pjxpwqkw_400x400.jpeg" + }, + "testimonialPosition": "MetaMask Founder", + "testimonialText": "Once we have good end-user caching of partial blockchain state (Like via \n@laconicnetwork), we could enable users to do lookups on a local phishing list, gaining stronger privacy benefits for much lower cost than a full node", + "testimonialUsername": "@Danfinlay" + } + ] + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/homePageTestimonialQueryResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/homePageTestimonialQueryResult.json new file mode 100644 index 0000000..bedafff --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/homePageTestimonialQueryResult.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53676939", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1660570305-t1qh-epl_400x400.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "Here’s a few of my favorite slept on protocols at the moment:\n\n@ComposableFin\n@laconicnetwork\n@archwayHQ\n@SeiNetwork\n@Shade_Protocol\n \nEach doing industry leading work in their respective niche and poised to gain significant market share during the bear market and beyond", + "testimonialUsername": "@DougieDeLuca" + } + } +} \ No newline at end of file diff --git a/json/_datocms_migration_refrence/__apiExplorerQueryResults/pressPageAllPressReleasesQueryResult.json b/json/_datocms_migration_refrence/__apiExplorerQueryResults/pressPageAllPressReleasesQueryResult.json new file mode 100644 index 0000000..eda8dc4 --- /dev/null +++ b/json/_datocms_migration_refrence/__apiExplorerQueryResults/pressPageAllPressReleasesQueryResult.json @@ -0,0 +1,5 @@ +{ + "data": { + "allPressReleases": [] + } +} \ No newline at end of file diff --git a/json/site_content/__readme.txt b/json/site_content/__readme.txt new file mode 100644 index 0000000..cb3cd3b --- /dev/null +++ b/json/site_content/__readme.txt @@ -0,0 +1,4 @@ +========NEXT_PUBLIC_DATOCMS_BYPASS /json/site_content/_readme.txt +-The JSON files in this directory and subdirectories feed the site per the naming conventions +-anything prefixed with an underscore is ignored +-directories represent content types which are listable rather than single pages (blogPosts, teams) \ No newline at end of file diff --git a/json/site_content/_blankPage.json b/json/site_content/_blankPage.json new file mode 100644 index 0000000..e69de29 diff --git a/json/site_content/aboutPage.json b/json/site_content/aboutPage.json new file mode 100644 index 0000000..ca17c3d --- /dev/null +++ b/json/site_content/aboutPage.json @@ -0,0 +1,56 @@ +{ + "data": { + "aboutPage": { + "heroCtaLabel": "", + "heroCtaLink": "", + "heroDescB01": "For blockchain technology to achieve true decentralization and wider adoption, we need to expand data access, verifiability, and control for developers and end users around the world.", + "heroDescB02": "Led by a globally renowned team of architects and core contributors across Ethereum, IPLD / IPFS, and Cosmos SDK, our mission is to accelerate blockchain development, interoperability, and adoption by providing decentralized application (DApp) developers and users with greater access to verifiable data.", + "heroHeading": "Accelerating Web3", + "heroTitle": "We’re materializing a decentralized and interoperable future, now", + "teamHeading": "Team", + "teamDescB01": "Five years in development, Laconic is built by platform experts and core contributors across Ethereum, IPLD / IPFS, and Cosmos SDK. ", + "teamDescB02": "Our project teams include Ethereum architects and developers who co-authored EIP-1559, and technology veterans who have launched products used by billions of people.", + "whitepaperCtaLink": "", + "whitepaperCtaText": "", + "whitepaperHeading": "Whitepaper", + "whitepaperImage": { + "url": "/images/site_content/aboutPage/whitepaperImage.png" + }, + "whitepaperLine": "Coming Soon", + "roadmapHeading": "Roadmap", + "roadmapQuarter01": { + "heading": "Q3 2022", + "details": [ + "Marketing & Community Launch", + "7 Founding Members", + "Laconic Golang SDK / Watchers 1" + ] + }, + "roadmapQuarter02": { + "heading": "Q4 2022", + "details": [ + "Announce Funding", + "Ethereum Mainnet Support", + "Updated Whitepaper" + ] + }, + "roadmapQuarter03": { + "heading": "Q1 2023", + "details": [ + "Laconic App v1", + "Laconic JS SDK", + "Watchers v2" + ] + }, + "roadmapQuarter04": { + "heading": "Q2 2023", + "details": [ + "Public Incentivized Testnet", + "Laconic App: Investor Services", + "Service Provider Registry v1", + "Community Grants Plan" + ] + } + } + } +} \ No newline at end of file diff --git a/json/site_content/author/__readme.txt b/json/site_content/author/__readme.txt new file mode 100644 index 0000000..d8e7ed5 --- /dev/null +++ b/json/site_content/author/__readme.txt @@ -0,0 +1,5 @@ +========NEXT_PUBLIC_DATOCMS_BYPASS /json/site_content/author/_readme.txt +-The JSON files in this directory were added for completion when migrating away from datocms +-To my knowledge this specific node is a dead end that is not linked to or iterated in any way +-That is to say, there is no linking out to a specific 'author' page +-The node only exists in the /json/site_content/author/blogPosts JSON files \ No newline at end of file diff --git a/json/site_content/author/_author.json b/json/site_content/author/_author.json new file mode 100644 index 0000000..ea6fbfb --- /dev/null +++ b/json/site_content/author/_author.json @@ -0,0 +1,8 @@ +{ + "data": { + "author": { + "id": "12345678", + "name": "Author Name" + } + } +} \ No newline at end of file diff --git a/json/site_content/author/christoph-berger.json b/json/site_content/author/christoph-berger.json new file mode 100644 index 0000000..dcc7684 --- /dev/null +++ b/json/site_content/author/christoph-berger.json @@ -0,0 +1,8 @@ +{ + "data": { + "author": { + "id": "7023106", + "name": "Christoph Berger" + } + } +} \ No newline at end of file diff --git a/json/site_content/author/joshua-eustis.json b/json/site_content/author/joshua-eustis.json new file mode 100644 index 0000000..c67fb15 --- /dev/null +++ b/json/site_content/author/joshua-eustis.json @@ -0,0 +1,8 @@ +{ + "data": { + "author": { + "id": "55641250", + "name": "Joshua Eustis" + } + } +} \ No newline at end of file diff --git a/json/site_content/author/maly-ly.json b/json/site_content/author/maly-ly.json new file mode 100644 index 0000000..bb9e4b6 --- /dev/null +++ b/json/site_content/author/maly-ly.json @@ -0,0 +1,8 @@ +{ + "data": { + "author": { + "id": "63259964", + "name": "Maly Ly" + } + } +} \ No newline at end of file diff --git a/json/site_content/author/michael-gushansky.json b/json/site_content/author/michael-gushansky.json new file mode 100644 index 0000000..598c662 --- /dev/null +++ b/json/site_content/author/michael-gushansky.json @@ -0,0 +1,8 @@ +{ + "data": { + "author": { + "id": "55457832", + "name": "Michael Gushansky" + } + } +} \ No newline at end of file diff --git a/json/site_content/author/robert-douglass.json b/json/site_content/author/robert-douglass.json new file mode 100644 index 0000000..7125fea --- /dev/null +++ b/json/site_content/author/robert-douglass.json @@ -0,0 +1,8 @@ +{ + "data": { + "author": { + "id": "44855867", + "name": "Robert Douglass" + } + } +} \ No newline at end of file diff --git a/json/site_content/author/sefan-adolf.json b/json/site_content/author/sefan-adolf.json new file mode 100644 index 0000000..3796612 --- /dev/null +++ b/json/site_content/author/sefan-adolf.json @@ -0,0 +1,8 @@ +{ + "data": { + "author": { + "id": "55512259", + "name": "Stefan Adolf" + } + } +} \ No newline at end of file diff --git a/json/site_content/author/zach-ramsay.json b/json/site_content/author/zach-ramsay.json new file mode 100644 index 0000000..1ea580c --- /dev/null +++ b/json/site_content/author/zach-ramsay.json @@ -0,0 +1,8 @@ +{ + "data": { + "author": { + "id": "63992470", + "name": "Zach Ramsay" + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/99-problems-but-nfts-aint-one.json b/json/site_content/blogPost/99-problems-but-nfts-aint-one.json new file mode 100644 index 0000000..c45871b --- /dev/null +++ b/json/site_content/blogPost/99-problems-but-nfts-aint-one.json @@ -0,0 +1,695 @@ +{ + "data": { + "blogPost": { + "slug": "99-problems-but-nfts-aint-one", + "title": "99 Problems But NFTs Ain’t One", + "date": "2022-09-28", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "/images/site_content/blogPost/99-problems-but-nfts-aint-one.png" + }, + "content": { + "blocks": [ + { + "id": "55644653", + "title": "Tweet" + }, + { + "id": "55644654", + "label": "Discord", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ask most people what they know about NFTs, and it’s likely you’ll hear a lot about digital art, tokenized authenticity, proof of ownership, and/or a hot new asset class (or, depending who you hang out with, commodity fetishism and the death knell of late-stage capitalism). But hot takes aside, today’s market for generative artwork is only the tip of the iceberg. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whatever the use case—and the possibilities are vast—today’s NFT ecosystem is generating mountains of largely unstructured data, both on and off chain. And without established technical standards to ensure clear structure and verifiability, it’s all too easy for that data to become the stuff of developer nightmares." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "More possibilities, more problems." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As use cases for NFTs continue to expand beyond " + }, + { + "url": "https://superrare.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "1/1 works of art" + } + ] + }, + { + "type": "span", + "value": " and " + }, + { + "url": "https://boredapeyachtclub.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "10k pfp collections" + } + ] + }, + { + "type": "span", + "value": ", developers building applications that integrate on-chain tokens face a widening maze of challenges around data management and queries. . The power of an NFT lies in its ability to represent any unique entity, with today’s common use cases including in-game tokens, editioned generative artwork, event ticketing, and even " + }, + { + "url": "https://www.theblock.co/post/134923/artist-blows-up-lamborghini-to-make-nfts-in-protest-against-crypto-culture", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "physical works or goods" + } + ] + }, + { + "type": "span", + "value": ". Blockchain and non-fungible tokens, in this case, make use of smart contracts, which can be written in a variety of programming languages, using varying methods of metadata storage and retrieval. The digital asset to which a given piece of metadata refers, and the metadata itself, can each be located almost anywhere in the decentralized web—which is itself both immeasurably large and continuously expanding. \n\nCombine a diversity of programming languages and an absence of standardization, and you end up with some novel problems, mostly involving how to handle the sheer volume of data generated, how to locate it in the many places it might be stored (both on chain and off), and how to handle inconsistent data formats and structures. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network was created to address these challenges through shared standards that make it possible for DApp developers to quickly and intuitively integrate and manage disparate NFT data and assets. In this piece, the first of a two-part series, we look at five major NFT implementation and integration issues—along with what we’re doing to solve each one, so you can sleep at night. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "1. NFT data lacks consistency and integrity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The functionality of an NFT lies in the metadata describing the individual item it represents. That metadata can consist of traits describing the characteristics of a jpeg artwork, essential information about copyright and intellectual property, guidelines for the item’s intended presentation, or all of the above and more. Metadata can also address a broad set of questions:" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": " Am I allowed to use an NFT for commercial purposes based on IP and copyright? Can I play a specific video, given its file format and codec? Are there readable methods for rendering this generative work? Can I easily list this NFT on the larger NFT marketplaces, where I have the best chance of selling it for the best price and in a timely manner?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The rapid growth of the NFT market has further expanded the possible functions of metadata. Developers need an efficient way to retrieve all types of metadata, and to maintain correlation with their on-chain counterparts in provable, hash-linked data structures. Laconic Watchers—APIs that serve data from the Laconic Network—fill this need. The Watchers’ custom search and caching services collect variously constructed data and combine it into a unified form that DApps can interpret and use, without sacrificing data integrity." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "2. NFT data is fragmented and scattered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s not practical to use current blockchain technology as a data storage or retrieval protocol. It was designed primarily as a means for achieving trustless consensus, not as a data availability system. The assets to which NFTs refer are often stored in protocols such as " + }, + { + "url": "https://www.arweave.org/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Arweave" + } + ] + }, + { + "type": "span", + "value": " and " + }, + { + "url": "https://ipfs.tech/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "IPFS" + } + ] + }, + { + "type": "span", + "value": ", and pointed to by the NFT’s “" + }, + { + "url": "https://docs.openzeppelin.com/contracts/2.x/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "tokenURI" + } + ] + }, + { + "type": "span", + "value": ".”  In one recent example, complete rendering libraries are stored as " + }, + { + "url": "https://twitter.com/dhof/status/1569509636587528195?s=20&t=KUYxKh-vG7qFaTtCvhRObA", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "compressed, on-chain data URIs" + } + ] + }, + { + "type": "span", + "value": "; smart contracts then access these libraries to render fully on-chain generative artworks." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A problem emerges, however, whenever a DApp needs to access any of this information. Methods for retrieval and DApp ingestion vary wildly depending on data type and location. And while RPC services can locate data—for a price—in most cases there’s no measurable way to ensure its integrity. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Despite being the most lightweight element of the Laconic Stack, the Laconic Watcher has the power to alleviate the proof issue, by preserving evidence of proof across data transformations while querying a far smaller subset of data than is typically required." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "3. For both NFTs and equivalent token formats, data types vary from blockchain to blockchain." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Most of today’s NFTs are based on Ethereum, and most commonly written in Solidity. But zoom out for a wider view of the possibilities for both token types and blockchains, and the problem space increases correspondingly. On Tezos, for instance, smart contracts are most often written in SmartPy or LIGO, with third-place Michelson being a common low-level, domain-specific language. On Solana, Rust, C, and C++ are commonly used to compose smart contracts (referred to in the Solana ecosystem as “programs).” It’s safe to say that we have more than a small naming, language, and methodology mess on our hands in the blockchain ecosystem!\n\nLet’s imagine a DApp that tracks all NFTs representing a certain kind of media—books, for example—to provide an index of on-chain literature. It would need the ability to accurately interpret smart contracts from each chain, across widely varying programming languages, syntax, and metadata standards." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network drastically simplifies this process, offering developers a unified view of data while allowing DApps to agnostically query data from multiple blockchains from a decentralized, content-addressable database." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "4. NFT games require lightning-fast retrieval and scalability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The volume of NFT transactions is expected to rise significantly with the " + }, + { + "url": "https://tiktok.immutable.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "current influx" + } + ] + }, + { + "type": "span", + "value": " of " + }, + { + "url": "https://about.instagram.com/blog/announcements/instagram-digital-collectibles", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "institutional Web 2 players" + } + ] + }, + { + "type": "span", + "value": " into the Web 3 ecosystem. The problem: As transaction volume and speed increase, data availability with censorship resistance and proof of integrity becomes increasingly unsustainable. And blockchain-based games alone are poised to send NFT transaction volumes to stratospheric heights, with more and more in-game events and transactions driving mounting network traffic. For example, the popular game " + }, + { + "url": "https://godsunchained.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Gods Unchained" + } + ] + }, + { + "type": "span", + "value": " is already generating " + }, + { + "url": "https://chainplay.gg/games/gods-unchained/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "significant transaction volume" + } + ] + }, + { + "type": "span", + "value": "—and it’s just one of countless on-chain games. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The growth of games and social apps using on-chain transactions continues to expose issues with Ethereum’s scalability and speed. The problem is compounded by the fact that most indexing services are typically a few blocks behind with updates—too far back to have DApps react to in-game events on time. And that leaves players holding the bag, subjected to unnecessarily clunky and unwieldy gaming experiences." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike traditional blockchain indexing services, the Laconic Network is equipped to provide up-to-date blockchain data with trivial delay, for scenarios in which real-time data retrieval is essential to user experience." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "5. Data retrieved from multiple locations is difficult to verify. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A fundamental promise of blockchain is verifiability. And while on-chain data is verifiable " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "per se" + }, + { + "type": "span", + "value": ", NFT data can be stored in any number of data repositories. Meanwhile, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "using" + }, + { + "type": "span", + "value": " that data requires intermediaries such as DNS system records, traditional web servers, and files stored in Web2 datacenters. Every one of these exposes the data to the possibility of censorship, manipulation by bad-faith actors, or simple disappearance. In such scenarios, associated NFT data is most often tied to a token via a ”tokenURI” that references the location of a JSON file containing token metadata. That file, in turn,  lives in one of these off-chain data storage protocols.\n\nTypical " + }, + { + "url": "https://ethereum.org/en/developers/docs/apis/json-rpc/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "RPC" + } + ] + }, + { + "type": "span", + "value": " providers can retrieve information about such data, then provide it to a client. A wallet client, for example, can find any media files associated with a particular NFT and display them. But this creates a trust bottleneck in the Web 3 ecosystem—requiring DApps and their users to trust the source of the data without proof. Solving this trust point is one of the primary goals of Laconic." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Today’s NFT ecosystem isn’t providing standards. Enter Laconic." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s clear that across blockchains, tokenization standards vary widely. Even with similar token types on the same chain, we see countless examples of how and where data is stored:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": " " + } + ] + }, + { + "item": "55644653", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nStay tuned for Part 2, where we’ll dive into more detail on how Laconic solves common implementation and integration challenges, offering both collectors and developers a far smoother NFT experience. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Got questions? Join us on Discord.\n" + } + ] + }, + { + "item": "55644654", + "type": "block" + } + ] + } + } + }, + "featured": false, + "id": "55644655", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/_blogPost.json b/json/site_content/blogPost/_blogPost.json new file mode 100644 index 0000000..3b54208 --- /dev/null +++ b/json/site_content/blogPost/_blogPost.json @@ -0,0 +1,148 @@ +{ + "data": { + "blogPost": { + "slug": "blog-post-slug", + "title": "Blog Post Title", + "date": "2023-04-17", + "category": [ + { + "id": "1111111", + "slug": "category-a-slug", + "title": "Category A Title" + }, + { + "id": "2222222", + "slug": "category-b-slug", + "title": "Category B Title" + } + ], + "author": { + "id": "12345678", + "name": "Author Name" + }, + "image": { + "url": "/images/site_content/blogPost/blog-post-slug.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Main content, stored in the obtuse JSON markup format pulled from datocms." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This is how you make " + }, + { + "url": "https://google.com", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "a link to Google." + } + ] + } + ] + } + ] + } + } + }, + "featured": false, + "id": "12345678", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/_test-blog-post.json b/json/site_content/blogPost/_test-blog-post.json new file mode 100644 index 0000000..e64cee5 --- /dev/null +++ b/json/site_content/blogPost/_test-blog-post.json @@ -0,0 +1,148 @@ +{ + "data": { + "blogPost": { + "slug": "test-blog-post", + "title": "Test Blog Post", + "date": "2023-04-17", + "category": [ + { + "id": "1111111", + "slug": "developers", + "title": "Developers" + }, + { + "id": "2222222", + "slug": "category-b-slug", + "title": "Category B Title" + } + ], + "author": { + "id": "12345678", + "name": "Author Name" + }, + "image": { + "url": "/images/site_content/blogPost/test-blog-post.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Main content, stored in the obtuse JSON markup format pulled from datocms." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This is how you make " + }, + { + "url": "https://google.com", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "a link to Google." + } + ] + } + ] + } + ] + } + } + }, + "featured": false, + "id": "12345678", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/_testing/copy-a.json b/json/site_content/blogPost/_testing/copy-a.json new file mode 100644 index 0000000..0812900 --- /dev/null +++ b/json/site_content/blogPost/_testing/copy-a.json @@ -0,0 +1,1544 @@ +{ + "data": { + "blogPost": { + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "category": [ + { + "slug": "developers", + "title": "Developers", + "id": "6311820" + }, + { + "slug": "not-product", + "title": "Not Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In " + }, + { + "url": "https://www.laconic.com/blog/intro-to-the-laconic-stack", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the last blog post" + } + ] + }, + { + "type": "span", + "value": ", we introduced all the main components of the Laconic Stack. The first point of entry for any developer wishing to use Laconic is " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "span", + "value": ". It allows you to stand up a local fixturenet for testing purposes. Integrated directly into Stack Orchestrator are several \"stacks\" which work out of the box. Today, we'll be going over the ERC20 \"stack\" to provide an overview of some key components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You will accomplish the following:\n" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "stand up the core stack using Stack Orchestrator" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 token" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 Watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "send tokens to and from your local account to a new account on Metamask" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "use GraphQL to query the watcher for information about the token and accounts" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This tutorial assumes you are on a local machine (Mac or Linux). Trying it in the cloud requires additional configurations (e.g., opening ports) not covered here." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Pre-requisites" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "python3" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://www.python.org/downloads/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/get-docker/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker-compose" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/compose/install/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MetaMask " + }, + { + "url": "https://metamask.io/download/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "span", + "value": " in the supported browser of your choice." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If using a fresh Ubuntu Digital Ocean droplet, check out " + }, + { + "url": "https://github.com/LaconicNetwork/Laconic-Documentation/blob/staging/scripts/install-laconic-stack.sh", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this script" + } + ] + }, + { + "type": "span", + "value": " for a quick setup." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WARNING" + }, + { + "type": "span", + "value": ": if installing docker-compose via package manager (as opposed to Docker Desktop), you must install the plugin, e.g., on Linux:" + } + ] + }, + { + "code": "mkdir -p ~/.docker/cli-plugins\ncurl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose\nchmod +x ~/.docker/cli-plugins/docker-compose", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, install the latest release of Stack Orchestrator" + } + ] + }, + { + "code": "curl -L -o laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Give it permission:" + } + ] + }, + { + "code": "chmod +x laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Verify operation:" + } + ] + }, + { + "code": "./laconic-so \nUsage: python -m laconic-so [OPTIONS] COMMAND [ARGS]...\n\n Laconic Stack Orchestrator\n\nOptions:\n --stack TEXT specify a stack to build/deploy\n --quiet\n --verbose\n --dry-run\n --local-stack\n --debug\n --continue-on-error\n -h, --help Show this message and exit.\n\nCommands:\n build-containers build the set of containers required for a complete...\n build-npms build the set of npm packages required for a...\n deploy-system deploy a stack\n setup-repositories git clone the set of repositories required to build...\n version print tool version", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For a more permanent setup, move the binary to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "~/bin" + }, + { + "type": "span", + "value": "` and add it your `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PATH" + }, + { + "type": "span", + "value": "`." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so" + }, + { + "type": "span", + "value": "` CLI tool makes it easy to experiment with various components of the stack. It allows you to quickly and seamlessly experiment with watchers. Because it uses docker/docker-compose, several commands in this tutorial will leverage the ability to execute commands directly in the containers. This, for example, means that `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "yarn" + }, + { + "type": "span", + "value": "` doesn't need to be installed on your local machine." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Setup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Use the stack orchestrator to pull the core repositories:" + } + ] + }, + { + "code": "./laconic-so --stack erc20 setup-repositories", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see something like:" + } + ] + }, + { + "code": "Dev Root is: /root/cerc\nChecking: /root/cerc/go-ethereum: Needs to be fetched\n100%|####################################################################################################| 71.6k/71.6k [00:23<00:00, 3.10kB/s]\nChecking: /root/cerc/ipld-eth-db: Needs to be fetched\n100%|##########################################################################################################| 595/595 [00:00<00:00, 991B/s]\nChecking: /root/cerc/ipld-eth-server: Needs to be fetched\n100%|####################################################################################################| 25.5k/25.5k [00:06<00:00, 3.82kB/s]\nChecking: /root/cerc/watcher-ts: Needs to be fetched\n100%|####################################################################################################| 8.79k/8.79k [00:01<00:00, 4.49kB/s]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we'll build the docker images for each repo we just fetched." + } + ] + }, + { + "code": "./laconic-so --stack erc20 build-containers ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This process will take 10-15 minutes, go make a pot of coffee. The output will give you an idea of what's going on. Eventually, you'll see:" + } + ] + }, + { + "code": "Successfully built 77c75d57ad66\nSuccessfully tagged cerc/watcher-erc20:local", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, let's deploy this stack:" + } + ] + }, + { + "code": "./laconic-so --stack erc20deploy-system up", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The output will looks like this (ignore the warnings):" + } + ] + }, + { + "code": "WARN[0000] The \"eth_proxy_on_error\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_forward_eth_calls\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_http_path\" variable is not set. Defaulting to a blank string. \n[+] Running 23/23\n ⠿ ipld-eth-db Pulled 18.4s\n ⠿ 213ec9aee27d Already exists 0.0s\n ⠿ 85c3ef7cf9a6 Pull complete 0.7s\n ⠿ ac29cc04759a Pull complete 0.9s\n ⠿ 2a37e244d86b Pull complete 13.5s\n ⠿ 36d7202aa1cf Pull complete 13.8s\n ⠿ 3acdddb9790a Pull complete 13.9s\n ⠿ 9a938759f2bf Pull complete 14.1s\n ⠿ 5d65a6241248 Pull complete 14.2s\n ⠿ cee6999f074e Pull complete 14.4s\n ⠿ 20b12472cb73 Pull complete 14.8s\n ⠿ 65467bb36f5f Pull complete 16.2s\n ⠿ fe6050bae51d Pull complete 17.4s\n ⠿ 519306d43b4a Pull complete 17.9s\n ⠿ erc20-watcher-db Pulled 15.0s\n ⠿ 8921db27df28 Already exists 0.0s\n ⠿ eb286326f602 Pull complete 0.3s\n ⠿ 63139c77dd7e Pull complete 0.5s\n ⠿ 17baeacd3984 Pull complete 13.5s\n ⠿ 5f08b9782916 Pull complete 13.8s\n ⠿ a836be7ad658 Pull complete 14.0s\n ⠿ 1966853affaf Pull complete 14.2s\n ⠿ 4dc6d2c8dede Pull complete 14.4s\n[+] Running 8/8\n ⠿ Network laconic-30c27a9be20b005274dfc23fd7e90256_default Created 0.1s\n ⠿ Volume \"laconic-30c27a9be20b005274dfc23fd7e90256_erc20_watcher_db_data\" Created 0.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1 Healthy 33.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1 Healthy 34.8s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1 Started 32.7s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1 Started 33.1s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1 Healthy 53.5s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1 Started 54.3s", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's take stock of what just happened, we:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "cloned a bunch of repos: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so setup-repositories" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "built all of their docker images: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so build-containers" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed these images as services that know about each other: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so deploy-system up" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Take a look at all the running docker containers:" + } + ] + }, + { + "code": "docker ps", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You should see 6 containers:" + } + ] + }, + { + "code": "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n605ccf0e4461 cerc/watcher-erc20:local \"docker-entrypoint.s…\" 6 minutes ago Up 5 minutes (unhealthy) 0.0.0.0:3002->3001/tcp, 0.0.0.0:9002->9001/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1\n0a00a3a1bcd6 cerc/ipld-eth-db:local \"/app/startup_script…\" 6 minutes ago Up 5 minutes laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1\nf4aece866e48 cerc/ipld-eth-server:local \"/app/entrypoint.sh\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8081-8082->8081-8082/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1\nebe0dc8cd2b4 cerc/go-ethereum-foundry:local \"./start-private-net…\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8545-8546->8545-8546/tcp laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1\n72263d100b8c postgres:14-alpine \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 0.0.0.0:15433->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1\nd2effc54624c timescale/timescaledb:2.8.1-pg14 \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 127.0.0.1:8077->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, via the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "watcher-erc20" + }, + { + "type": "span", + "value": "` container, the " + }, + { + "url": "https://graphql.org/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "span", + "value": " playground is enabled on " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and you should check that it is there:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great so now we have the core stack up and running, let's deploy an ERC20 token." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, we need the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER ID" + }, + { + "type": "span", + "value": "` of the ERC20 watcher:" + } + ] + }, + { + "code": "docker ps | grep \"watcher-erc20\"", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Using the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ID" + }, + { + "type": "span", + "value": "` from the example above, we'll export the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER_ID" + }, + { + "type": "span", + "value": "` for use throughout the rest of the tutorial:" + } + ] + }, + { + "code": "export CONTAINER_ID=605ccf0e4461", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we can deploy an ERC20 token (currency symbol GLD):" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:deploy:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and your output should look like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-deploy\nDownloading compiler 0.8.0\nCompiled 5 Solidity files successfully\nGLD Token deployed to: 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\nDeployed at block: 9087 0x4dc63b4b2695b644d7d390d70c9de0232399ea4d54b8c75911eee14c13f9ceaf\nDone in 157.39s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, now that we've deployed the GLD token, you'll want to export its address for later use:" + } + ] + }, + { + "code": "export TOKEN_ADDRESS=0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Get your primary account address with:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn account:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and the following output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker account\n0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nDone in 21.63s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "export that address to your shell:" + } + ] + }, + { + "code": "export PRIMARY_ADDRESS=0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To get the latest block hash at any time, run:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "for an output like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker block-latest\nBlock Number: 12783\nBlock Hash: 0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\nDone in 21.44s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll configure MetaMask." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "MetaMask" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Open MetaMask in your browser:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Add Network\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Scroll to the bottow and click \"Add Network Manually\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Put in this information:" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you see the error above \"This URL is currently used by the Localhost 8545 Network\", change `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "localhost" + }, + { + "type": "span", + "value": "` to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "127.0.0.1" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We will come back to MetaMask later and complete this process; for now, copy your new address" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and export it for later:" + } + ] + }, + { + "code": "export RECIPIENT_ADDRESS=0x988a070c97D33a9Dfcc134df5628b77e8B5214ad", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Head on over to " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and paste the following (but with your variables):" + } + ] + }, + { + "code": "query {\n name(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n symbol(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n totalSupply(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "then click \"Run\" and you'll see a response like this:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"name\": {\n \"value\": \"Gold\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzavwb52aq6smf6movgcimvuoggp3cifayb2vyidg3ar546pwtb3dea\\\",\\\"ipldBlock\\\":\\\"0xf843a032575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85ba1a0476f6c6400000000000000000000000000000000000000000000000000000008\\\"}}}]\"\n }\n },\n \"symbol\": {\n \"value\": \"GLD\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzanp5bxcn6wqd2yptbbwo5o4rx3mhpji43yd7sfd42suq6hjuhuroq\\\",\\\"ipldBlock\\\":\\\"0xf843a03a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19ba1a0474c440000000000000000000000000000000000000000000000000000000006\\\"}}}]\"\n }\n },\n \"totalSupply\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzasfla7wzuessejihdtrxqd5lqxc57egukbbricizz2ssrltex4uvq\\\",\\\"ipldBlock\\\":\\\"0xeca0305787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace8a893635c9adc5dea00000\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here's what it'll look like in the browser:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A lot has happened thus far, so let's review; we've:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "downloaded the core repos, built their docker images, and launched a local network (all using stack orchestrator)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed an ERC20 token, added it to our MetaMask account" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "used the GraphQL playground to query the ERC20 watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "exported a handful of shell variables which are about to come in handy" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll use the playground to query account balances:" + } + ] + }, + { + "code": "query {\n fromBalanceOf: balanceOf(\n # latest block hash\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # primary account having all the balance initially\n # created by stack orchestrator\n owner: \"0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\"\n ) {\n value\n proof {\n data\n }\n }\n toBalanceOf: balanceOf(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # address copied from MetaMask, has no balance initially\n owner: \"0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "the primary address should have " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " 1000000000000000000000 and the recipient address should have 0:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzarsopkngjoijjyktfhgckq7te4dsk25gfyj653uxu4kcwqoyuykiq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5dea00000\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"0\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"\\\",\\\"ipldBlock\\\":\\\"0x\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note also that the recipient address also does not yet have a `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "cid" + }, + { + "type": "span", + "value": "` or `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ipldBlock" + }, + { + "type": "span", + "value": "`, which makes sense; this is a random account we just created and hasn't received any transactions. The network does not know about it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send it some GLD!" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:transfer:docker --token $TOKEN_ADDRESS --to $RECIPIENT_ADDRESS --amount 100000000 ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see a familiar output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-transfer --token 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550 --to 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad --amount 100000000\nNothing to compile\nTransfer Event at block: 13371 0x412dbc25599773bfe929c67882e4a001b9d1b3b8e1c60ad4a495d5306608c77a\nfrom: 0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nto: 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\nvalue: 100000000\nDone in 26.12s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now get the latest block hash:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and go back to the GraphQL playground. If you've changed nothing since the last query, update only the latest block hash and run the query again, you'll see the updated account balances:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"999999999999900000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzahg5shtf2rr7pompx7yx6r22zu4lea7ftlnbqkqcabvndsrvoljhq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5d8aa1f00\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"100000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzas6xotntgq4u3v4eui6pmtbyttgikzmu7mppknam2wrekhoynupjq\\\",\\\"ipldBlock\\\":\\\"0xe7a03305adb1a8efab310b21e03d5a9f08d8cf98815372c2c4d8068e1359b8f996bc858405f5e100\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, you've now used a watcher to see query token balances." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send some tokens from the MetaMask recipient account back to the primary account." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Recall that when adding the network to MetaMask, we used the currency symbol \"GLD\". However, this does not mean that MetaMask can auto-detect the token, therefore, we will have to manually import it:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Copy the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TOKEN_ADDRESS" + }, + { + "type": "span", + "value": "` and paste it in the popup. The two other fields should auto-complete:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Import Token\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and now you'll see your balance. Ignore the GLD token from earlier." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, send some tokens back to the primary address using MetaMask:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Make the gas price `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "0" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Grab the latest block hash (again) and fire off the GraphQL query for account balances to see the change." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Voila! You've successfully stood up the core Laconic stack, deployed an ERC20 token, and queried account balances." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cleanup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tear down your docker containers with:" + } + ] + }, + { + "code": "./laconic-so deploy-system --stack erc20 down", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Next steps" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Try out the " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator/tree/main/app/data/stacks/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "ERC721 demo" + } + ] + } + ] + } + ] + } + } + }, + "date": "2023-01-31", + "featured": false, + "id": "64023526", + "image": { + "url": "https://www.datocms-assets.com/66113/1675106863-laconic_clippy_watcher_02.png" + }, + "slug": "copy-a", + "title": "COPY A" + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/_testing/copy-b.json b/json/site_content/blogPost/_testing/copy-b.json new file mode 100644 index 0000000..d5426c0 --- /dev/null +++ b/json/site_content/blogPost/_testing/copy-b.json @@ -0,0 +1,529 @@ +{ + "data": { + "blogPost": { + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "category": [ + { + "slug": "fake", + "title": "Fake", + "id": "6311820" + } + ], + "content": { + "blocks": [ + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here’s the main problem: reading data from the Ethereum blockchains is either cheap and sloppy or expensive and correct. As a result, Dapp developers have come to rely on inexpensive centralized services that do not provide evidence to verify the correctness of the data they are serving to Dapps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only is it expensive to get verifiable data but it can also be challenging to parse out the subset of data you really need. In the early days of SQL, you had to be proficient at the command line in order to use the product, and so use was limited to those that had that specialized capability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Eventually, GUIs were built such that anyone with basic computer skills could use drop-down menus and create database schemas without writing a single line of code. Web3 is still in the early days of SQL, it is not easy onboarding new users and developers who are otherwise quite capable with the latest Web2 technologies.\n\nRight now, there’s all this data on Ethereum and as a Dapp developer, you only want a tiny fraction of it. But, to verify that fraction, you have to (among several other things) maintain an archive node - this is prohibitively expensive for the majority of developers. To solve this problem, centralized services such as (Infura, The Graph, and Alchemy) have popped up and currently account for the majority (if not most) of Dapp queries to Ethereum." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic was created to address these and several other problems in the blockchain ecosystem. Not only does Laconic make it easy to get verifiable data - quickly and cheaply - it also provides a framework for data transformation and aggregation that are difficult or impossible to do in other systems.\n\nArchitecting a solution to this requires many moving pieces; these have been developed by core Ethereum & Cosmos contributors over the past 5 years. In this post, we will walk you through the various components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are three different ways to participate in the Laconic Network: Member Validators, Service Providers, and Dapp Developers. To describe the responsibilities and benefits of each role, we must first start grounded in the technicalities of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let’s take a look at the following core stack diagram:" + } + ] + }, + { + "item": "63992474", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note: this diagram intentionally leaves out several repositories (e.g., codecs, utilities, rpc shims). This is done for simplicity reasons and anyone diving deep into the stack will discover them.\n\nThe two repositories at the top are also the main entry points for most developers. `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` is a command-line tool for, well, orchestrating the stack. It uses docker-compose to deploy a specified collection of networked docker containers, thereby eliminating the need to set up a variety of services independently. Every user of the Laconic Stack will at some point - if not regularly - use the stack orchestrator." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watchers-ts" + } + ] + }, + { + "type": "span", + "value": "` repo contains the publically available Watchers and the code to generate them. Watchers are TypeScript that is generated from one or more Solidity smart contracts. Dapp Developers can participate in the Laconic Network by either 1) writing a custom watcher for their Dapp or 2) writing a generally useful watcher and publishing it to the Laconic Registry, thus earning a fee every time it is used. We’ll come back to Watchers later." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Down at the bottom left is the `" + }, + { + "url": "https://github.com/cerc-io/laconicd", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconicd" + } + ] + }, + { + "type": "span", + "value": "` repository and it is indeed the “bottom” of the stack. It is built from the Cosmos SDK and has custom modules specific to operating the Laconic Network (e.g., fork of Ethermint/Evmos, auction, nameservice). It is likely that in the future there will be a public testnet, however, because the Laconic Network is a permissioned validator set, only Member Validators that have officially joined the Laconic Network will be included in the mainnet. Just because the validator set is permissioned does not prohibit anyone from running a full node and Service Providers or others may choose to do so for a variety of reasons." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/laconic-sdk", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-sdk" + } + ] + }, + { + "type": "span", + "value": "` is a library for facilitating talking to `laconicd`. Both the `" + }, + { + "url": "https://github.com/cerc-io/laconic-registry-cli", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-registry-cli" + } + ] + }, + { + "type": "span", + "value": "` and the `" + }, + { + "url": "https://github.com/cerc-io/laconic-console", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-console" + } + ] + }, + { + "type": "span", + "value": "` use it. While `laconic-registry-cli` is a command-line tool for doing so, the `laconic-console` is a user interface for writing and reading records on the Laconic Network. These general-purpose tools are useful for a wide variety of use cases across the stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A key goal of the Laconic Network is to provide accurate, verifiable data from the Ethereum blockchain. Comparisons to currently available solutions are for another post, however, no service currently exists to provide inexpensive evidence that the data being served is correct. The Laconic solution (one of) to this is in something called “statediffing”, a part of the stack run by Member Validators and, likely by Service Providers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It starts with a maintained fork of `" + }, + { + "url": "https://github.com/cerc-io/go-ethereum", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "go-ethereum (geth)" + } + ] + }, + { + "type": "span", + "value": "` that has an added real-time state-diffing service. Statediffing gives a clear picture of the state between any given blockheights. This allows Laconic to minimize the amount of computation required for providing proofs. Three additional “helper” services perform different tasks required to get a full picture of the state as required by an application. Together, these comprise the Full Index Node (FIN)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-service" + } + ] + }, + { + "type": "span", + "value": "` provides historical state data, while the `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-fill-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-fill-service" + } + ] + }, + { + "type": "span", + "value": "` uses the historical state data to fill statediff gaps as required. Finally, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-state-snapshot", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-state-snapshot" + } + ] + }, + { + "type": "span", + "value": "` loads a complete state at a certain blockheight, which helps to bootstrap the system. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nThere is more to be written about statediffing, however, what’s important to note here is that each service is writing independently to `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-db", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-db" + } + ] + }, + { + "type": "span", + "value": "`. The latter serves as a bucket for state data that has been indexed in IPLD. Rather than querying this database directly, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-server", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-server" + } + ] + }, + { + "type": "span", + "value": "` provides an API layer for Watchers to easily query relevant pieces of data from the Ethereum state. Additionally, `ipld-eth-server` recapitulates the native Ethereum JSON RPC interfaces on top of the `ipld-eth-db` database." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "And so we’ve come full circle back to the Watchers. As previously mentioned, they are generated from one or more Solidity smart contracts and configured to query specific pieces of data relevant to a Dapp. Watchers make it easy to query the data you need from Ethereum " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "and" + }, + { + "type": "span", + "value": " - along the way - get evidence in order to generate proofs that your data is correct. This is in contrast to currently available solutions for Dapp developers, who must currently rely on centralized providers that don’t provide evidence to generate proofs." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Web3 is (still!) in its early days, and like the early days of SQL, Dapp developers need advanced knowledge of complex data structures to build their Dapp. Watchers simplify this by exposing a GraphQL endpoint, a solution familiar to an order of magnitude more developers than querying the Ethereum blockchain directly." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is building a suite of tools to address core problems in Web3. Today, we’ve provided an overview of the main components of the Laconic Stack. Developers interested in Laconic should start with `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` to get a sense of running different parts of the stack, then check out `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watcher-ts" + } + ] + }, + { + "type": "span", + "value": "` to experiment with different watchers and progress to making their own." + } + ] + } + ] + } + } + }, + "date": "1999-01-18", + "featured": false, + "id": "63992475", + "image": { + "url": "https://www.datocms-assets.com/66113/1673986992-laconic_clippy_grid2.png" + }, + "slug": "copy-b", + "title": "COPY B" + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/_testing/copy-c.json b/json/site_content/blogPost/_testing/copy-c.json new file mode 100644 index 0000000..1d0d0ea --- /dev/null +++ b/json/site_content/blogPost/_testing/copy-c.json @@ -0,0 +1,389 @@ +{ + "data": { + "blogPost": { + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "category": [ + { + "slug": "insights", + "title": "Insights", + "id": "6311819" + }, + { + "slug": "fake", + "title": "Fake", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic cofounder Rick Dudley appeared on a special livestream of The Interop with host Sebastien Couture to discuss the Laconic Stack, the blockchain data problems that Laconic solves, Laconic’s novel governance structure, and how Laconic can index and verify data faster, more efficiently, and at lower cost. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Below is a distilled transcript of Rick’s responses during the discussion." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Future is App Chains" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think there will be millions of chains, and we'll be using a combination of rollups and mesh–not straight linear L1, L2, L3, but also meshes of rollups and attestations publishing bridges, etc. And although we may have millions of chains, we won't have millions of massive chains. A large chain may have 100 members, and there may be one or two chains out there with 4,000 validators. But in the world, you only need a few of those." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think everything becomes an app chain. I think mainnet Ethereum ultimately becomes an app chain and the application is settling rollups–very similar to Cosmos Hub, frankly. Polkadot, Ethereum 2.0, Cosmos Hub are all actually very similar in terms of the endgame state in the final thesis. And I don't think that there will necessarily be a winner per se. I think they will have curious different properties. " + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Why Laconic?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The ultimate goal of Laconic is to get all of the data that a user is concerned about in the hands of that user. Not in a cloud-hosted environment, not in Microsoft, not in AWS, but in users’ actual custody. And to enable them to do all the verification themselves." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Right now, it’s very difficult to extract parts of data from the Ethereum Mainnet that are relevant for Dapp needs. It’s almost impossible to synchronize a Geth node in a reasonable amount of time." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are multiple light client protocols that have come around to help alleviate this problem but they still don't go all the way. The Laconic Network goes the whole way. It goes from source code, to what is in the user's eyeballs with everything being verifiable. If you see a message on Laconic that came to you through the Laconic Network, you could say, \"I want to know which blockchain or blockchains this came from. I want to know what code generated this result. I want to know who wrote that code.” We provide all of that in the Laconic Network." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Three Major Components of Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is the Laconic LLC itself, which is in the Cayman Islands. There is the Laconic Stack, which is the standalone software that anyone can run today to generate this data and the evidence that they need. And then there's the Laconic Network, which facilitates the buying and selling of data. It facilitates running these services, discovering the services, paying for services, and then making sure all of that is verifiable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Those three components are an evolution. We've iterated on the Stack many times over at this point. MakerDAO is still using an early version of that stack to this day last I checked, which was recently. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you were an intrepid developer, you could go into the Stack Orchestrator code and run that yourself and put that into production yourself right now. But the problem with that is it's very expensive to generate this evidence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s computationally very expensive, and specifically, disk I/O operations are very expensive activities to do. So as a Dapp developer, when you have very few users, you can run this reasonably on the laptop. But as your app grows, or if you're wanting to see all of the Uniswap V3 pool data, then a laptop's not going to be able to process that in a timely manner necessarily. I mean, laptops are pretty powerful so some of them can, but maybe not all of them. And at that point, you need hardware. And when you need hardware, you then have this problem of, \"Okay, well am I going to buy hardware and rack it in a data center?\" That's probably not a viable answer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Am I going to go to AWS? Well, AWS is centralized, there are all sorts of problems. There's censorship for instance. AWS may choose to comply with a law that I'm not legally obligated to comply with. We've seen this issue with Alchemy and Infura, and these solutions comply with the laws in their jurisdiction, but the Dapp developer is in a different jurisdiction. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So then you end up with this situation; \"Okay, well if I want to have multiple service providers actually serving this data to users, they need to be in multiple jurisdictions.\" And that's what Laconic LLC solves. It's a Cayman Island LLC. We have members in different jurisdictions and those members will contract with the end users and comply with those laws in that way." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and Cosmos" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic team members were also core contributors to Cosmos SDK–we did a bunch of work on the Cosmos SDK. The data structures in Ethereum and the data structures in Cosmos and many other blockchains were designed to facilitate consensus, not to facilitate reading the data back out. And so in those architectures, there's utility in taking the techniques that we've applied to Ethereum and applying them to those other chains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is a value and utility to taking those techniques and applying them to the Cosmos SDK chains. Osmosis is an example of where it would be useful. For example, you can't have a block explorer that works across Cosmos Hub upgrades. No one's ever bothered to build one that works that way. If you built the block explorer on top of Laconic instead of directly on top of the chain, you would actually be able to provide that continuity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Every time a Cosmos chain upgrades, they regenesis and restart the chain. When you start that new genesis, people–just as a matter of convenience–don't preserve that data. You don't have a way of representing the irregular state change that happens during the upgrade. Whereas in the Laconic system, we have a means of doing all those things. We can link any two arbitrary chains together and we have a means of representing these arbitrary state changes. We can provide that continuity as a service." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentive Alignment " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because we're IPLD based, we actually can relatively easily take our archive and push it into Filecoin, where there can then be this clear monetization strategy for storing the data. Because we monetize the transmission of the data, which is a much easier problem to solve than the verifiable storage of Filecoin, we're providing an incentive for why someone would do that. Think about it. There are different incentives throughout the process. There's an incentive for including the transaction. That incentive is very clear, but there's not really any incentive in any blockchain I'm aware of for why I should then send that data. Why should I satisfy a read from a user? A user asks for a read, and why do I care?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That's what Laconic is trying to solve–we're incentivizing the reading of that data. And by incentivizing the reading of that data, that's step number two. Now we can talk about the incentives of step number three, which is a long-term persistent storage of that data. Because if you think about just having the incentives of just Filecoin and just Ethereum, you have this gap in the middle. Why do I take the Ethereum data and transform that and publish it to Filecoin? There's not really an incentive for me to do that." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whereas with Laconic, there starts to become more of an incentive to do that because I need to support my own read infrastructure. People will come to you and know to come to a single place to get their historic reads as well as their more recent reads. And so you’ll be incentivized to charge them. There will already be an ecosystem in place where people are accustomed to paying for data. And when they want to pay for old data or new data, they'll come to the same place, buy that data, and that will incentivize archival storage. Right now we don't have a very good model for why archival storage persists. And it is a real mechanism design issue actually." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "InterPlanetary Linked Data (IPLD) is the core of our system. The first thing we do is take the Ethereum data, which could be any blockchain or any hash linked data structure, and we convert that into an IPLD object. We then index it in that context. We’re storing the RLP encoded bytes, but we are also storing the CID (Content ID), the multi-format address of that object. That's how we're able to generate evidence." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On Ethereum, you have transaction receipts and you have the event messages. When you have an event message on Ethereum, the event message does not prove all the way back up to the root. So when you have a set of events, which is what The Graph consumes, the way that you prove that event is correct is that you find the block that that event was in, and then you rerun that whole block and at the end of it you see if you have the same event that you started with. Whereas, if I have an account balance on Ethereum, I have a block number and then I can get a proof. So I don't have to recompute the whole block to figure out the account balance in that block. I just get the proof from the Ethereum client about that account balance at that block, and I can present that proof and the balance to the user using eth_getProof." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But the actual logs in Ethereum are not provable in this way. This is why The Graph isn't provable and there are a lot of consequences from this. But because we use IPLD, we can create those hash links. Where the link was missing in the original Ethereum protocol, we can augment that protocol and generate a proof using the Ethereum data and our additional links, which are relatively easily. It's not some weird, crazy different format. It's this format that is very similar to the existing Ethereum formats, that prove that this log actually came from this block." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Member Validators" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic L2 has seven Founding Members right now. These seven Members validate, ingest the blocks, and make commitments to the state of those blocks. They then share that information with a paying customer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are plans to increase the validator set. From a customer perspective, if our customer is a Dapp developer and they're saying, “right now I have to use Infura, Alchemy, Blocknative to assert that my data is correct because if one of them goes down for whatever reason, that's three right there.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That sounds like a pain in the ass. With Laconic, you integrate one protocol and you get seven Member Validators instead of three, and you get an assertion from us that you can verify yourself that we're actually physically located in different places. Alchemy and Infura both run in AWS, I presume. If AWS goes down, you just lost two out of your three, if not all three out of your three in that case. Seven is a low number, but seven is incredibly high compared to what people have right now, or they think they have four and they have one, whereas we're positively asserting that you have seven." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "RPC Services and Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the path to building Watchers, we realized we had to build extremely performant RPC endpoints, and we had to build out a deployment system. We realized that that was actually what people wanted to buy from us. Most Dapps don’t want to bother with Watchers right now. What they want to see is this immediate savings on the RPC endpoint side. From there, oncet our foot is in the door, we can say, \"Well, we can give you even more savings. You are using that RPC endpoint to build your own indexer. We have a whole library of tools to build indexers that will auto-generate indexers for you. And we have a marketplace where you can go to get other people to run that indexer for you when you don't want to scale it.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Currently, RPC endpoints are subsidized by VCs. Dapp developers are never experiencing the true cost of running an indexing service or running an RPC endpoint. They're not exposed to that in a free market way. There's this actor, this venture capitalist, who is going in and giving away free samples at a massive scale. The challenge for us is in how we compete with that? There's also a challenge in that our customers are depending on this centralization service and don't realize it. " + } + ] + } + ] + } + } + }, + "date": "2023-02-09", + "featured": false, + "id": "64080923", + "image": { + "url": "https://www.datocms-assets.com/66113/1675901476-laconic-seb-blog.png" + }, + "slug": "copy-c", + "title": "COPY C" + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/_testing/erc20-watcher-demo.json b/json/site_content/blogPost/_testing/erc20-watcher-demo.json new file mode 100644 index 0000000..10a8e73 --- /dev/null +++ b/json/site_content/blogPost/_testing/erc20-watcher-demo.json @@ -0,0 +1,1544 @@ +{ + "data": { + "blogPost": { + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "category": [ + { + "slug": "developers", + "title": "Developers", + "id": "6311820" + }, + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In " + }, + { + "url": "https://www.laconic.com/blog/intro-to-the-laconic-stack", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the last blog post" + } + ] + }, + { + "type": "span", + "value": ", we introduced all the main components of the Laconic Stack. The first point of entry for any developer wishing to use Laconic is " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "span", + "value": ". It allows you to stand up a local fixturenet for testing purposes. Integrated directly into Stack Orchestrator are several \"stacks\" which work out of the box. Today, we'll be going over the ERC20 \"stack\" to provide an overview of some key components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You will accomplish the following:\n" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "stand up the core stack using Stack Orchestrator" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 token" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 Watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "send tokens to and from your local account to a new account on Metamask" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "use GraphQL to query the watcher for information about the token and accounts" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This tutorial assumes you are on a local machine (Mac or Linux). Trying it in the cloud requires additional configurations (e.g., opening ports) not covered here." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Pre-requisites" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "python3" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://www.python.org/downloads/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/get-docker/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker-compose" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/compose/install/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MetaMask " + }, + { + "url": "https://metamask.io/download/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "span", + "value": " in the supported browser of your choice." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If using a fresh Ubuntu Digital Ocean droplet, check out " + }, + { + "url": "https://github.com/LaconicNetwork/Laconic-Documentation/blob/staging/scripts/install-laconic-stack.sh", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this script" + } + ] + }, + { + "type": "span", + "value": " for a quick setup." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WARNING" + }, + { + "type": "span", + "value": ": if installing docker-compose via package manager (as opposed to Docker Desktop), you must install the plugin, e.g., on Linux:" + } + ] + }, + { + "code": "mkdir -p ~/.docker/cli-plugins\ncurl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose\nchmod +x ~/.docker/cli-plugins/docker-compose", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, install the latest release of Stack Orchestrator" + } + ] + }, + { + "code": "curl -L -o laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Give it permission:" + } + ] + }, + { + "code": "chmod +x laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Verify operation:" + } + ] + }, + { + "code": "./laconic-so \nUsage: python -m laconic-so [OPTIONS] COMMAND [ARGS]...\n\n Laconic Stack Orchestrator\n\nOptions:\n --stack TEXT specify a stack to build/deploy\n --quiet\n --verbose\n --dry-run\n --local-stack\n --debug\n --continue-on-error\n -h, --help Show this message and exit.\n\nCommands:\n build-containers build the set of containers required for a complete...\n build-npms build the set of npm packages required for a...\n deploy-system deploy a stack\n setup-repositories git clone the set of repositories required to build...\n version print tool version", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For a more permanent setup, move the binary to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "~/bin" + }, + { + "type": "span", + "value": "` and add it your `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PATH" + }, + { + "type": "span", + "value": "`." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so" + }, + { + "type": "span", + "value": "` CLI tool makes it easy to experiment with various components of the stack. It allows you to quickly and seamlessly experiment with watchers. Because it uses docker/docker-compose, several commands in this tutorial will leverage the ability to execute commands directly in the containers. This, for example, means that `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "yarn" + }, + { + "type": "span", + "value": "` doesn't need to be installed on your local machine." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Setup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Use the stack orchestrator to pull the core repositories:" + } + ] + }, + { + "code": "./laconic-so --stack erc20 setup-repositories", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see something like:" + } + ] + }, + { + "code": "Dev Root is: /root/cerc\nChecking: /root/cerc/go-ethereum: Needs to be fetched\n100%|####################################################################################################| 71.6k/71.6k [00:23<00:00, 3.10kB/s]\nChecking: /root/cerc/ipld-eth-db: Needs to be fetched\n100%|##########################################################################################################| 595/595 [00:00<00:00, 991B/s]\nChecking: /root/cerc/ipld-eth-server: Needs to be fetched\n100%|####################################################################################################| 25.5k/25.5k [00:06<00:00, 3.82kB/s]\nChecking: /root/cerc/watcher-ts: Needs to be fetched\n100%|####################################################################################################| 8.79k/8.79k [00:01<00:00, 4.49kB/s]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we'll build the docker images for each repo we just fetched." + } + ] + }, + { + "code": "./laconic-so --stack erc20 build-containers ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This process will take 10-15 minutes, go make a pot of coffee. The output will give you an idea of what's going on. Eventually, you'll see:" + } + ] + }, + { + "code": "Successfully built 77c75d57ad66\nSuccessfully tagged cerc/watcher-erc20:local", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, let's deploy this stack:" + } + ] + }, + { + "code": "./laconic-so --stack erc20deploy-system up", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The output will looks like this (ignore the warnings):" + } + ] + }, + { + "code": "WARN[0000] The \"eth_proxy_on_error\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_forward_eth_calls\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_http_path\" variable is not set. Defaulting to a blank string. \n[+] Running 23/23\n ⠿ ipld-eth-db Pulled 18.4s\n ⠿ 213ec9aee27d Already exists 0.0s\n ⠿ 85c3ef7cf9a6 Pull complete 0.7s\n ⠿ ac29cc04759a Pull complete 0.9s\n ⠿ 2a37e244d86b Pull complete 13.5s\n ⠿ 36d7202aa1cf Pull complete 13.8s\n ⠿ 3acdddb9790a Pull complete 13.9s\n ⠿ 9a938759f2bf Pull complete 14.1s\n ⠿ 5d65a6241248 Pull complete 14.2s\n ⠿ cee6999f074e Pull complete 14.4s\n ⠿ 20b12472cb73 Pull complete 14.8s\n ⠿ 65467bb36f5f Pull complete 16.2s\n ⠿ fe6050bae51d Pull complete 17.4s\n ⠿ 519306d43b4a Pull complete 17.9s\n ⠿ erc20-watcher-db Pulled 15.0s\n ⠿ 8921db27df28 Already exists 0.0s\n ⠿ eb286326f602 Pull complete 0.3s\n ⠿ 63139c77dd7e Pull complete 0.5s\n ⠿ 17baeacd3984 Pull complete 13.5s\n ⠿ 5f08b9782916 Pull complete 13.8s\n ⠿ a836be7ad658 Pull complete 14.0s\n ⠿ 1966853affaf Pull complete 14.2s\n ⠿ 4dc6d2c8dede Pull complete 14.4s\n[+] Running 8/8\n ⠿ Network laconic-30c27a9be20b005274dfc23fd7e90256_default Created 0.1s\n ⠿ Volume \"laconic-30c27a9be20b005274dfc23fd7e90256_erc20_watcher_db_data\" Created 0.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1 Healthy 33.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1 Healthy 34.8s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1 Started 32.7s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1 Started 33.1s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1 Healthy 53.5s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1 Started 54.3s", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's take stock of what just happened, we:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "cloned a bunch of repos: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so setup-repositories" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "built all of their docker images: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so build-containers" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed these images as services that know about each other: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so deploy-system up" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Take a look at all the running docker containers:" + } + ] + }, + { + "code": "docker ps", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You should see 6 containers:" + } + ] + }, + { + "code": "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n605ccf0e4461 cerc/watcher-erc20:local \"docker-entrypoint.s…\" 6 minutes ago Up 5 minutes (unhealthy) 0.0.0.0:3002->3001/tcp, 0.0.0.0:9002->9001/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1\n0a00a3a1bcd6 cerc/ipld-eth-db:local \"/app/startup_script…\" 6 minutes ago Up 5 minutes laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1\nf4aece866e48 cerc/ipld-eth-server:local \"/app/entrypoint.sh\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8081-8082->8081-8082/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1\nebe0dc8cd2b4 cerc/go-ethereum-foundry:local \"./start-private-net…\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8545-8546->8545-8546/tcp laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1\n72263d100b8c postgres:14-alpine \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 0.0.0.0:15433->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1\nd2effc54624c timescale/timescaledb:2.8.1-pg14 \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 127.0.0.1:8077->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, via the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "watcher-erc20" + }, + { + "type": "span", + "value": "` container, the " + }, + { + "url": "https://graphql.org/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "span", + "value": " playground is enabled on " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and you should check that it is there:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great so now we have the core stack up and running, let's deploy an ERC20 token." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, we need the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER ID" + }, + { + "type": "span", + "value": "` of the ERC20 watcher:" + } + ] + }, + { + "code": "docker ps | grep \"watcher-erc20\"", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Using the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ID" + }, + { + "type": "span", + "value": "` from the example above, we'll export the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER_ID" + }, + { + "type": "span", + "value": "` for use throughout the rest of the tutorial:" + } + ] + }, + { + "code": "export CONTAINER_ID=605ccf0e4461", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we can deploy an ERC20 token (currency symbol GLD):" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:deploy:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and your output should look like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-deploy\nDownloading compiler 0.8.0\nCompiled 5 Solidity files successfully\nGLD Token deployed to: 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\nDeployed at block: 9087 0x4dc63b4b2695b644d7d390d70c9de0232399ea4d54b8c75911eee14c13f9ceaf\nDone in 157.39s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, now that we've deployed the GLD token, you'll want to export its address for later use:" + } + ] + }, + { + "code": "export TOKEN_ADDRESS=0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Get your primary account address with:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn account:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and the following output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker account\n0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nDone in 21.63s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "export that address to your shell:" + } + ] + }, + { + "code": "export PRIMARY_ADDRESS=0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To get the latest block hash at any time, run:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "for an output like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker block-latest\nBlock Number: 12783\nBlock Hash: 0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\nDone in 21.44s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll configure MetaMask." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "MetaMask" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Open MetaMask in your browser:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Add Network\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Scroll to the bottow and click \"Add Network Manually\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Put in this information:" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you see the error above \"This URL is currently used by the Localhost 8545 Network\", change `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "localhost" + }, + { + "type": "span", + "value": "` to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "127.0.0.1" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We will come back to MetaMask later and complete this process; for now, copy your new address" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and export it for later:" + } + ] + }, + { + "code": "export RECIPIENT_ADDRESS=0x988a070c97D33a9Dfcc134df5628b77e8B5214ad", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Head on over to " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and paste the following (but with your variables):" + } + ] + }, + { + "code": "query {\n name(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n symbol(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n totalSupply(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "then click \"Run\" and you'll see a response like this:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"name\": {\n \"value\": \"Gold\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzavwb52aq6smf6movgcimvuoggp3cifayb2vyidg3ar546pwtb3dea\\\",\\\"ipldBlock\\\":\\\"0xf843a032575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85ba1a0476f6c6400000000000000000000000000000000000000000000000000000008\\\"}}}]\"\n }\n },\n \"symbol\": {\n \"value\": \"GLD\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzanp5bxcn6wqd2yptbbwo5o4rx3mhpji43yd7sfd42suq6hjuhuroq\\\",\\\"ipldBlock\\\":\\\"0xf843a03a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19ba1a0474c440000000000000000000000000000000000000000000000000000000006\\\"}}}]\"\n }\n },\n \"totalSupply\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzasfla7wzuessejihdtrxqd5lqxc57egukbbricizz2ssrltex4uvq\\\",\\\"ipldBlock\\\":\\\"0xeca0305787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace8a893635c9adc5dea00000\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here's what it'll look like in the browser:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A lot has happened thus far, so let's review; we've:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "downloaded the core repos, built their docker images, and launched a local network (all using stack orchestrator)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed an ERC20 token, added it to our MetaMask account" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "used the GraphQL playground to query the ERC20 watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "exported a handful of shell variables which are about to come in handy" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll use the playground to query account balances:" + } + ] + }, + { + "code": "query {\n fromBalanceOf: balanceOf(\n # latest block hash\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # primary account having all the balance initially\n # created by stack orchestrator\n owner: \"0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\"\n ) {\n value\n proof {\n data\n }\n }\n toBalanceOf: balanceOf(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # address copied from MetaMask, has no balance initially\n owner: \"0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "the primary address should have " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " 1000000000000000000000 and the recipient address should have 0:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzarsopkngjoijjyktfhgckq7te4dsk25gfyj653uxu4kcwqoyuykiq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5dea00000\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"0\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"\\\",\\\"ipldBlock\\\":\\\"0x\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note also that the recipient address also does not yet have a `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "cid" + }, + { + "type": "span", + "value": "` or `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ipldBlock" + }, + { + "type": "span", + "value": "`, which makes sense; this is a random account we just created and hasn't received any transactions. The network does not know about it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send it some GLD!" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:transfer:docker --token $TOKEN_ADDRESS --to $RECIPIENT_ADDRESS --amount 100000000 ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see a familiar output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-transfer --token 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550 --to 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad --amount 100000000\nNothing to compile\nTransfer Event at block: 13371 0x412dbc25599773bfe929c67882e4a001b9d1b3b8e1c60ad4a495d5306608c77a\nfrom: 0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nto: 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\nvalue: 100000000\nDone in 26.12s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now get the latest block hash:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and go back to the GraphQL playground. If you've changed nothing since the last query, update only the latest block hash and run the query again, you'll see the updated account balances:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"999999999999900000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzahg5shtf2rr7pompx7yx6r22zu4lea7ftlnbqkqcabvndsrvoljhq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5d8aa1f00\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"100000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzas6xotntgq4u3v4eui6pmtbyttgikzmu7mppknam2wrekhoynupjq\\\",\\\"ipldBlock\\\":\\\"0xe7a03305adb1a8efab310b21e03d5a9f08d8cf98815372c2c4d8068e1359b8f996bc858405f5e100\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, you've now used a watcher to see query token balances." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send some tokens from the MetaMask recipient account back to the primary account." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Recall that when adding the network to MetaMask, we used the currency symbol \"GLD\". However, this does not mean that MetaMask can auto-detect the token, therefore, we will have to manually import it:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Copy the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TOKEN_ADDRESS" + }, + { + "type": "span", + "value": "` and paste it in the popup. The two other fields should auto-complete:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Import Token\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and now you'll see your balance. Ignore the GLD token from earlier." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, send some tokens back to the primary address using MetaMask:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Make the gas price `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "0" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Grab the latest block hash (again) and fire off the GraphQL query for account balances to see the change." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Voila! You've successfully stood up the core Laconic stack, deployed an ERC20 token, and queried account balances." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cleanup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tear down your docker containers with:" + } + ] + }, + { + "code": "./laconic-so deploy-system --stack erc20 down", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Next steps" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Try out the " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator/tree/main/app/data/stacks/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "ERC721 demo" + } + ] + } + ] + } + ] + } + } + }, + "date": "2023-01-31", + "featured": false, + "id": "64023526", + "image": { + "url": "https://www.datocms-assets.com/66113/1675106863-laconic_clippy_watcher_02.png" + }, + "slug": "erc20-watcher-demo", + "title": "[TEST EDIT TO LOCAL JSON] ERC20 Watcher Demo" + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/_testing/intro-to-the-laconic-stack.json b/json/site_content/blogPost/_testing/intro-to-the-laconic-stack.json new file mode 100644 index 0000000..653a5be --- /dev/null +++ b/json/site_content/blogPost/_testing/intro-to-the-laconic-stack.json @@ -0,0 +1,529 @@ +{ + "data": { + "blogPost": { + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "category": [ + { + "slug": "developers", + "title": "Developers", + "id": "6311820" + } + ], + "content": { + "blocks": [ + {} + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here’s the main problem: reading data from the Ethereum blockchains is either cheap and sloppy or expensive and correct. As a result, Dapp developers have come to rely on inexpensive centralized services that do not provide evidence to verify the correctness of the data they are serving to Dapps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only is it expensive to get verifiable data but it can also be challenging to parse out the subset of data you really need. In the early days of SQL, you had to be proficient at the command line in order to use the product, and so use was limited to those that had that specialized capability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Eventually, GUIs were built such that anyone with basic computer skills could use drop-down menus and create database schemas without writing a single line of code. Web3 is still in the early days of SQL, it is not easy onboarding new users and developers who are otherwise quite capable with the latest Web2 technologies.\n\nRight now, there’s all this data on Ethereum and as a Dapp developer, you only want a tiny fraction of it. But, to verify that fraction, you have to (among several other things) maintain an archive node - this is prohibitively expensive for the majority of developers. To solve this problem, centralized services such as (Infura, The Graph, and Alchemy) have popped up and currently account for the majority (if not most) of Dapp queries to Ethereum." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic was created to address these and several other problems in the blockchain ecosystem. Not only does Laconic make it easy to get verifiable data - quickly and cheaply - it also provides a framework for data transformation and aggregation that are difficult or impossible to do in other systems.\n\nArchitecting a solution to this requires many moving pieces; these have been developed by core Ethereum & Cosmos contributors over the past 5 years. In this post, we will walk you through the various components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are three different ways to participate in the Laconic Network: Member Validators, Service Providers, and Dapp Developers. To describe the responsibilities and benefits of each role, we must first start grounded in the technicalities of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let’s take a look at the following core stack diagram:" + } + ] + }, + { + "item": "63992474", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note: this diagram intentionally leaves out several repositories (e.g., codecs, utilities, rpc shims). This is done for simplicity reasons and anyone diving deep into the stack will discover them.\n\nThe two repositories at the top are also the main entry points for most developers. `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` is a command-line tool for, well, orchestrating the stack. It uses docker-compose to deploy a specified collection of networked docker containers, thereby eliminating the need to set up a variety of services independently. Every user of the Laconic Stack will at some point - if not regularly - use the stack orchestrator." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watchers-ts" + } + ] + }, + { + "type": "span", + "value": "` repo contains the publically available Watchers and the code to generate them. Watchers are TypeScript that is generated from one or more Solidity smart contracts. Dapp Developers can participate in the Laconic Network by either 1) writing a custom watcher for their Dapp or 2) writing a generally useful watcher and publishing it to the Laconic Registry, thus earning a fee every time it is used. We’ll come back to Watchers later." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Down at the bottom left is the `" + }, + { + "url": "https://github.com/cerc-io/laconicd", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconicd" + } + ] + }, + { + "type": "span", + "value": "` repository and it is indeed the “bottom” of the stack. It is built from the Cosmos SDK and has custom modules specific to operating the Laconic Network (e.g., fork of Ethermint/Evmos, auction, nameservice). It is likely that in the future there will be a public testnet, however, because the Laconic Network is a permissioned validator set, only Member Validators that have officially joined the Laconic Network will be included in the mainnet. Just because the validator set is permissioned does not prohibit anyone from running a full node and Service Providers or others may choose to do so for a variety of reasons." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/laconic-sdk", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-sdk" + } + ] + }, + { + "type": "span", + "value": "` is a library for facilitating talking to `laconicd`. Both the `" + }, + { + "url": "https://github.com/cerc-io/laconic-registry-cli", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-registry-cli" + } + ] + }, + { + "type": "span", + "value": "` and the `" + }, + { + "url": "https://github.com/cerc-io/laconic-console", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-console" + } + ] + }, + { + "type": "span", + "value": "` use it. While `laconic-registry-cli` is a command-line tool for doing so, the `laconic-console` is a user interface for writing and reading records on the Laconic Network. These general-purpose tools are useful for a wide variety of use cases across the stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A key goal of the Laconic Network is to provide accurate, verifiable data from the Ethereum blockchain. Comparisons to currently available solutions are for another post, however, no service currently exists to provide inexpensive evidence that the data being served is correct. The Laconic solution (one of) to this is in something called “statediffing”, a part of the stack run by Member Validators and, likely by Service Providers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It starts with a maintained fork of `" + }, + { + "url": "https://github.com/cerc-io/go-ethereum", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "go-ethereum (geth)" + } + ] + }, + { + "type": "span", + "value": "` that has an added real-time state-diffing service. Statediffing gives a clear picture of the state between any given blockheights. This allows Laconic to minimize the amount of computation required for providing proofs. Three additional “helper” services perform different tasks required to get a full picture of the state as required by an application. Together, these comprise the Full Index Node (FIN)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-service" + } + ] + }, + { + "type": "span", + "value": "` provides historical state data, while the `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-fill-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-fill-service" + } + ] + }, + { + "type": "span", + "value": "` uses the historical state data to fill statediff gaps as required. Finally, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-state-snapshot", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-state-snapshot" + } + ] + }, + { + "type": "span", + "value": "` loads a complete state at a certain blockheight, which helps to bootstrap the system. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nThere is more to be written about statediffing, however, what’s important to note here is that each service is writing independently to `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-db", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-db" + } + ] + }, + { + "type": "span", + "value": "`. The latter serves as a bucket for state data that has been indexed in IPLD. Rather than querying this database directly, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-server", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-server" + } + ] + }, + { + "type": "span", + "value": "` provides an API layer for Watchers to easily query relevant pieces of data from the Ethereum state. Additionally, `ipld-eth-server` recapitulates the native Ethereum JSON RPC interfaces on top of the `ipld-eth-db` database." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "And so we’ve come full circle back to the Watchers. As previously mentioned, they are generated from one or more Solidity smart contracts and configured to query specific pieces of data relevant to a Dapp. Watchers make it easy to query the data you need from Ethereum " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "and" + }, + { + "type": "span", + "value": " - along the way - get evidence in order to generate proofs that your data is correct. This is in contrast to currently available solutions for Dapp developers, who must currently rely on centralized providers that don’t provide evidence to generate proofs." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Web3 is (still!) in its early days, and like the early days of SQL, Dapp developers need advanced knowledge of complex data structures to build their Dapp. Watchers simplify this by exposing a GraphQL endpoint, a solution familiar to an order of magnitude more developers than querying the Ethereum blockchain directly." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is building a suite of tools to address core problems in Web3. Today, we’ve provided an overview of the main components of the Laconic Stack. Developers interested in Laconic should start with `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` to get a sense of running different parts of the stack, then check out `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watcher-ts" + } + ] + }, + { + "type": "span", + "value": "` to experiment with different watchers and progress to making their own." + } + ] + } + ] + } + } + }, + "date": "2023-01-18", + "featured": false, + "id": "63992475", + "image": { + "url": "https://www.datocms-assets.com/66113/1673986992-laconic_clippy_grid2.png" + }, + "slug": "intro-to-the-laconic-stack", + "title": "[TEST EDIT TO LOCAL JSON] Intro to the Laconic Stack" + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/_testing/rick-dudley-on-the-interop.json b/json/site_content/blogPost/_testing/rick-dudley-on-the-interop.json new file mode 100644 index 0000000..ca672be --- /dev/null +++ b/json/site_content/blogPost/_testing/rick-dudley-on-the-interop.json @@ -0,0 +1,389 @@ +{ + "data": { + "blogPost": { + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "category": [ + { + "slug": "insights", + "title": "Insights", + "id": "6311819" + }, + { + "slug": "product", + "title": "Product", + "id": "3545003" + } + ], + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic cofounder Rick Dudley appeared on a special livestream of The Interop with host Sebastien Couture to discuss the Laconic Stack, the blockchain data problems that Laconic solves, Laconic’s novel governance structure, and how Laconic can index and verify data faster, more efficiently, and at lower cost. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Below is a distilled transcript of Rick’s responses during the discussion." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Future is App Chains" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think there will be millions of chains, and we'll be using a combination of rollups and mesh–not straight linear L1, L2, L3, but also meshes of rollups and attestations publishing bridges, etc. And although we may have millions of chains, we won't have millions of massive chains. A large chain may have 100 members, and there may be one or two chains out there with 4,000 validators. But in the world, you only need a few of those." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think everything becomes an app chain. I think mainnet Ethereum ultimately becomes an app chain and the application is settling rollups–very similar to Cosmos Hub, frankly. Polkadot, Ethereum 2.0, Cosmos Hub are all actually very similar in terms of the endgame state in the final thesis. And I don't think that there will necessarily be a winner per se. I think they will have curious different properties. " + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Why Laconic?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The ultimate goal of Laconic is to get all of the data that a user is concerned about in the hands of that user. Not in a cloud-hosted environment, not in Microsoft, not in AWS, but in users’ actual custody. And to enable them to do all the verification themselves." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Right now, it’s very difficult to extract parts of data from the Ethereum Mainnet that are relevant for Dapp needs. It’s almost impossible to synchronize a Geth node in a reasonable amount of time." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are multiple light client protocols that have come around to help alleviate this problem but they still don't go all the way. The Laconic Network goes the whole way. It goes from source code, to what is in the user's eyeballs with everything being verifiable. If you see a message on Laconic that came to you through the Laconic Network, you could say, \"I want to know which blockchain or blockchains this came from. I want to know what code generated this result. I want to know who wrote that code.” We provide all of that in the Laconic Network." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Three Major Components of Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is the Laconic LLC itself, which is in the Cayman Islands. There is the Laconic Stack, which is the standalone software that anyone can run today to generate this data and the evidence that they need. And then there's the Laconic Network, which facilitates the buying and selling of data. It facilitates running these services, discovering the services, paying for services, and then making sure all of that is verifiable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Those three components are an evolution. We've iterated on the Stack many times over at this point. MakerDAO is still using an early version of that stack to this day last I checked, which was recently. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you were an intrepid developer, you could go into the Stack Orchestrator code and run that yourself and put that into production yourself right now. But the problem with that is it's very expensive to generate this evidence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s computationally very expensive, and specifically, disk I/O operations are very expensive activities to do. So as a Dapp developer, when you have very few users, you can run this reasonably on the laptop. But as your app grows, or if you're wanting to see all of the Uniswap V3 pool data, then a laptop's not going to be able to process that in a timely manner necessarily. I mean, laptops are pretty powerful so some of them can, but maybe not all of them. And at that point, you need hardware. And when you need hardware, you then have this problem of, \"Okay, well am I going to buy hardware and rack it in a data center?\" That's probably not a viable answer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Am I going to go to AWS? Well, AWS is centralized, there are all sorts of problems. There's censorship for instance. AWS may choose to comply with a law that I'm not legally obligated to comply with. We've seen this issue with Alchemy and Infura, and these solutions comply with the laws in their jurisdiction, but the Dapp developer is in a different jurisdiction. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So then you end up with this situation; \"Okay, well if I want to have multiple service providers actually serving this data to users, they need to be in multiple jurisdictions.\" And that's what Laconic LLC solves. It's a Cayman Island LLC. We have members in different jurisdictions and those members will contract with the end users and comply with those laws in that way." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and Cosmos" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic team members were also core contributors to Cosmos SDK–we did a bunch of work on the Cosmos SDK. The data structures in Ethereum and the data structures in Cosmos and many other blockchains were designed to facilitate consensus, not to facilitate reading the data back out. And so in those architectures, there's utility in taking the techniques that we've applied to Ethereum and applying them to those other chains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is a value and utility to taking those techniques and applying them to the Cosmos SDK chains. Osmosis is an example of where it would be useful. For example, you can't have a block explorer that works across Cosmos Hub upgrades. No one's ever bothered to build one that works that way. If you built the block explorer on top of Laconic instead of directly on top of the chain, you would actually be able to provide that continuity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Every time a Cosmos chain upgrades, they regenesis and restart the chain. When you start that new genesis, people–just as a matter of convenience–don't preserve that data. You don't have a way of representing the irregular state change that happens during the upgrade. Whereas in the Laconic system, we have a means of doing all those things. We can link any two arbitrary chains together and we have a means of representing these arbitrary state changes. We can provide that continuity as a service." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentive Alignment " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because we're IPLD based, we actually can relatively easily take our archive and push it into Filecoin, where there can then be this clear monetization strategy for storing the data. Because we monetize the transmission of the data, which is a much easier problem to solve than the verifiable storage of Filecoin, we're providing an incentive for why someone would do that. Think about it. There are different incentives throughout the process. There's an incentive for including the transaction. That incentive is very clear, but there's not really any incentive in any blockchain I'm aware of for why I should then send that data. Why should I satisfy a read from a user? A user asks for a read, and why do I care?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That's what Laconic is trying to solve–we're incentivizing the reading of that data. And by incentivizing the reading of that data, that's step number two. Now we can talk about the incentives of step number three, which is a long-term persistent storage of that data. Because if you think about just having the incentives of just Filecoin and just Ethereum, you have this gap in the middle. Why do I take the Ethereum data and transform that and publish it to Filecoin? There's not really an incentive for me to do that." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whereas with Laconic, there starts to become more of an incentive to do that because I need to support my own read infrastructure. People will come to you and know to come to a single place to get their historic reads as well as their more recent reads. And so you’ll be incentivized to charge them. There will already be an ecosystem in place where people are accustomed to paying for data. And when they want to pay for old data or new data, they'll come to the same place, buy that data, and that will incentivize archival storage. Right now we don't have a very good model for why archival storage persists. And it is a real mechanism design issue actually." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "InterPlanetary Linked Data (IPLD) is the core of our system. The first thing we do is take the Ethereum data, which could be any blockchain or any hash linked data structure, and we convert that into an IPLD object. We then index it in that context. We’re storing the RLP encoded bytes, but we are also storing the CID (Content ID), the multi-format address of that object. That's how we're able to generate evidence." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On Ethereum, you have transaction receipts and you have the event messages. When you have an event message on Ethereum, the event message does not prove all the way back up to the root. So when you have a set of events, which is what The Graph consumes, the way that you prove that event is correct is that you find the block that that event was in, and then you rerun that whole block and at the end of it you see if you have the same event that you started with. Whereas, if I have an account balance on Ethereum, I have a block number and then I can get a proof. So I don't have to recompute the whole block to figure out the account balance in that block. I just get the proof from the Ethereum client about that account balance at that block, and I can present that proof and the balance to the user using eth_getProof." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But the actual logs in Ethereum are not provable in this way. This is why The Graph isn't provable and there are a lot of consequences from this. But because we use IPLD, we can create those hash links. Where the link was missing in the original Ethereum protocol, we can augment that protocol and generate a proof using the Ethereum data and our additional links, which are relatively easily. It's not some weird, crazy different format. It's this format that is very similar to the existing Ethereum formats, that prove that this log actually came from this block." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Member Validators" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic L2 has seven Founding Members right now. These seven Members validate, ingest the blocks, and make commitments to the state of those blocks. They then share that information with a paying customer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are plans to increase the validator set. From a customer perspective, if our customer is a Dapp developer and they're saying, “right now I have to use Infura, Alchemy, Blocknative to assert that my data is correct because if one of them goes down for whatever reason, that's three right there.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That sounds like a pain in the ass. With Laconic, you integrate one protocol and you get seven Member Validators instead of three, and you get an assertion from us that you can verify yourself that we're actually physically located in different places. Alchemy and Infura both run in AWS, I presume. If AWS goes down, you just lost two out of your three, if not all three out of your three in that case. Seven is a low number, but seven is incredibly high compared to what people have right now, or they think they have four and they have one, whereas we're positively asserting that you have seven." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "RPC Services and Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the path to building Watchers, we realized we had to build extremely performant RPC endpoints, and we had to build out a deployment system. We realized that that was actually what people wanted to buy from us. Most Dapps don’t want to bother with Watchers right now. What they want to see is this immediate savings on the RPC endpoint side. From there, oncet our foot is in the door, we can say, \"Well, we can give you even more savings. You are using that RPC endpoint to build your own indexer. We have a whole library of tools to build indexers that will auto-generate indexers for you. And we have a marketplace where you can go to get other people to run that indexer for you when you don't want to scale it.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Currently, RPC endpoints are subsidized by VCs. Dapp developers are never experiencing the true cost of running an indexing service or running an RPC endpoint. They're not exposed to that in a free market way. There's this actor, this venture capitalist, who is going in and giving away free samples at a massive scale. The challenge for us is in how we compete with that? There's also a challenge in that our customers are depending on this centralization service and don't realize it. " + } + ] + } + ] + } + } + }, + "date": "2023-02-09", + "featured": false, + "id": "64080923", + "image": { + "url": "https://www.datocms-assets.com/66113/1675901476-laconic-seb-blog.png" + }, + "slug": "rick-dudley-on-the-interop", + "title": "[TEST EDIT TO LOCAL JSON] Rick Dudley Discusses Laconic Network on The Interop" + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/erc20-watcher-demo.json b/json/site_content/blogPost/erc20-watcher-demo.json new file mode 100644 index 0000000..f0f38b5 --- /dev/null +++ b/json/site_content/blogPost/erc20-watcher-demo.json @@ -0,0 +1,1616 @@ +{ + "data": { + "blogPost": { + "slug": "erc20-watcher-demo", + "title": "ERC20 Watcher Demo", + "date": "2023-01-31", + "category": [ + { + "id": "6311820", + "slug": "developers", + "title": "Developers" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "image": { + "url": "/images/site_content/blogPost/erc20-watcher-demo.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In " + }, + { + "url": "https://www.laconic.com/blog/intro-to-the-laconic-stack", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the last blog post" + } + ] + }, + { + "type": "span", + "value": ", we introduced all the main components of the Laconic Stack. The first point of entry for any developer wishing to use Laconic is " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "rel", + "value": "noopener noreferrer" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "span", + "value": ". It allows you to stand up a local fixturenet for testing purposes. Integrated directly into Stack Orchestrator are several \"stacks\" which work out of the box. Today, we'll be going over the ERC20 \"stack\" to provide an overview of some key components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You will accomplish the following:\n" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "stand up the core stack using Stack Orchestrator" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 token" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deploy an ERC20 Watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "send tokens to and from your local account to a new account on Metamask" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "use GraphQL to query the watcher for information about the token and accounts" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This tutorial assumes you are on a local machine (Mac or Linux). Trying it in the cloud requires additional configurations (e.g., opening ports) not covered here." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Pre-requisites" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "python3" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://www.python.org/downloads/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/get-docker/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "`" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "docker-compose" + }, + { + "type": "span", + "value": "` " + }, + { + "url": "https://docs.docker.com/compose/install/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MetaMask " + }, + { + "url": "https://metamask.io/download/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Install" + } + ] + }, + { + "type": "span", + "value": " in the supported browser of your choice." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If using a fresh Ubuntu Digital Ocean droplet, check out " + }, + { + "url": "https://github.com/LaconicNetwork/Laconic-Documentation/blob/staging/scripts/install-laconic-stack.sh", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this script" + } + ] + }, + { + "type": "span", + "value": " for a quick setup." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WARNING" + }, + { + "type": "span", + "value": ": if installing docker-compose via package manager (as opposed to Docker Desktop), you must install the plugin, e.g., on Linux:" + } + ] + }, + { + "code": "mkdir -p ~/.docker/cli-plugins\ncurl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose\nchmod +x ~/.docker/cli-plugins/docker-compose", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, install the latest release of Stack Orchestrator" + } + ] + }, + { + "code": "curl -L -o laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Give it permission:" + } + ] + }, + { + "code": "chmod +x laconic-so", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Verify operation:" + } + ] + }, + { + "code": "./laconic-so \nUsage: python -m laconic-so [OPTIONS] COMMAND [ARGS]...\n\n Laconic Stack Orchestrator\n\nOptions:\n --stack TEXT specify a stack to build/deploy\n --quiet\n --verbose\n --dry-run\n --local-stack\n --debug\n --continue-on-error\n -h, --help Show this message and exit.\n\nCommands:\n build-containers build the set of containers required for a complete...\n build-npms build the set of npm packages required for a...\n deploy-system deploy a stack\n setup-repositories git clone the set of repositories required to build...\n version print tool version", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For a more permanent setup, move the binary to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "~/bin" + }, + { + "type": "span", + "value": "` and add it your `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PATH" + }, + { + "type": "span", + "value": "`." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stack Orchestrator" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so" + }, + { + "type": "span", + "value": "` CLI tool makes it easy to experiment with various components of the stack. It allows you to quickly and seamlessly experiment with watchers. Because it uses docker/docker-compose, several commands in this tutorial will leverage the ability to execute commands directly in the containers. This, for example, means that `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "yarn" + }, + { + "type": "span", + "value": "` doesn't need to be installed on your local machine." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Setup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Use the stack orchestrator to pull the core repositories:" + } + ] + }, + { + "code": "./laconic-so --stack erc20 setup-repositories", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see something like:" + } + ] + }, + { + "code": "Dev Root is: /root/cerc\nChecking: /root/cerc/go-ethereum: Needs to be fetched\n100%|####################################################################################################| 71.6k/71.6k [00:23<00:00, 3.10kB/s]\nChecking: /root/cerc/ipld-eth-db: Needs to be fetched\n100%|##########################################################################################################| 595/595 [00:00<00:00, 991B/s]\nChecking: /root/cerc/ipld-eth-server: Needs to be fetched\n100%|####################################################################################################| 25.5k/25.5k [00:06<00:00, 3.82kB/s]\nChecking: /root/cerc/watcher-ts: Needs to be fetched\n100%|####################################################################################################| 8.79k/8.79k [00:01<00:00, 4.49kB/s]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we'll build the docker images for each repo we just fetched." + } + ] + }, + { + "code": "./laconic-so --stack erc20 build-containers ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This process will take 10-15 minutes, go make a pot of coffee. The output will give you an idea of what's going on. Eventually, you'll see:" + } + ] + }, + { + "code": "Successfully built 77c75d57ad66\nSuccessfully tagged cerc/watcher-erc20:local", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, let's deploy this stack:" + } + ] + }, + { + "code": "./laconic-so --stack erc20deploy-system up", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The output will looks like this (ignore the warnings):" + } + ] + }, + { + "code": "WARN[0000] The \"eth_proxy_on_error\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_forward_eth_calls\" variable is not set. Defaulting to a blank string. \nWARN[0000] The \"eth_http_path\" variable is not set. Defaulting to a blank string. \n[+] Running 23/23\n ⠿ ipld-eth-db Pulled 18.4s\n ⠿ 213ec9aee27d Already exists 0.0s\n ⠿ 85c3ef7cf9a6 Pull complete 0.7s\n ⠿ ac29cc04759a Pull complete 0.9s\n ⠿ 2a37e244d86b Pull complete 13.5s\n ⠿ 36d7202aa1cf Pull complete 13.8s\n ⠿ 3acdddb9790a Pull complete 13.9s\n ⠿ 9a938759f2bf Pull complete 14.1s\n ⠿ 5d65a6241248 Pull complete 14.2s\n ⠿ cee6999f074e Pull complete 14.4s\n ⠿ 20b12472cb73 Pull complete 14.8s\n ⠿ 65467bb36f5f Pull complete 16.2s\n ⠿ fe6050bae51d Pull complete 17.4s\n ⠿ 519306d43b4a Pull complete 17.9s\n ⠿ erc20-watcher-db Pulled 15.0s\n ⠿ 8921db27df28 Already exists 0.0s\n ⠿ eb286326f602 Pull complete 0.3s\n ⠿ 63139c77dd7e Pull complete 0.5s\n ⠿ 17baeacd3984 Pull complete 13.5s\n ⠿ 5f08b9782916 Pull complete 13.8s\n ⠿ a836be7ad658 Pull complete 14.0s\n ⠿ 1966853affaf Pull complete 14.2s\n ⠿ 4dc6d2c8dede Pull complete 14.4s\n[+] Running 8/8\n ⠿ Network laconic-30c27a9be20b005274dfc23fd7e90256_default Created 0.1s\n ⠿ Volume \"laconic-30c27a9be20b005274dfc23fd7e90256_erc20_watcher_db_data\" Created 0.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1 Healthy 33.0s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1 Healthy 34.8s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1 Started 32.7s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1 Started 33.1s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1 Healthy 53.5s\n ⠿ Container laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1 Started 54.3s", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's take stock of what just happened, we:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "cloned a bunch of repos: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so setup-repositories" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "built all of their docker images: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so build-containers" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed these images as services that know about each other: `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "laconic-so deploy-system up" + }, + { + "type": "span", + "value": "`" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Take a look at all the running docker containers:" + } + ] + }, + { + "code": "docker ps", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You should see 6 containers:" + } + ] + }, + { + "code": "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n605ccf0e4461 cerc/watcher-erc20:local \"docker-entrypoint.s…\" 6 minutes ago Up 5 minutes (unhealthy) 0.0.0.0:3002->3001/tcp, 0.0.0.0:9002->9001/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-1\n0a00a3a1bcd6 cerc/ipld-eth-db:local \"/app/startup_script…\" 6 minutes ago Up 5 minutes laconic-30c27a9be20b005274dfc23fd7e90256-migrations-1\nf4aece866e48 cerc/ipld-eth-server:local \"/app/entrypoint.sh\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8081-8082->8081-8082/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-server-1\nebe0dc8cd2b4 cerc/go-ethereum-foundry:local \"./start-private-net…\" 6 minutes ago Up 5 minutes (healthy) 127.0.0.1:8545-8546->8545-8546/tcp laconic-30c27a9be20b005274dfc23fd7e90256-go-ethereum-1\n72263d100b8c postgres:14-alpine \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 0.0.0.0:15433->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-erc20-watcher-db-1\nd2effc54624c timescale/timescaledb:2.8.1-pg14 \"docker-entrypoint.s…\" 6 minutes ago Up 6 minutes (healthy) 127.0.0.1:8077->5432/tcp laconic-30c27a9be20b005274dfc23fd7e90256-ipld-eth-db-1", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, via the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "watcher-erc20" + }, + { + "type": "span", + "value": "` container, the " + }, + { + "url": "https://graphql.org/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "span", + "value": " playground is enabled on " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and you should check that it is there:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great so now we have the core stack up and running, let's deploy an ERC20 token." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, we need the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER ID" + }, + { + "type": "span", + "value": "` of the ERC20 watcher:" + } + ] + }, + { + "code": "docker ps | grep \"watcher-erc20\"", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Using the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ID" + }, + { + "type": "span", + "value": "` from the example above, we'll export the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "CONTAINER_ID" + }, + { + "type": "span", + "value": "` for use throughout the rest of the tutorial:" + } + ] + }, + { + "code": "export CONTAINER_ID=605ccf0e4461", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next, we can deploy an ERC20 token (currency symbol GLD):" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:deploy:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and your output should look like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-deploy\nDownloading compiler 0.8.0\nCompiled 5 Solidity files successfully\nGLD Token deployed to: 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\nDeployed at block: 9087 0x4dc63b4b2695b644d7d390d70c9de0232399ea4d54b8c75911eee14c13f9ceaf\nDone in 157.39s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, now that we've deployed the GLD token, you'll want to export its address for later use:" + } + ] + }, + { + "code": "export TOKEN_ADDRESS=0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Get your primary account address with:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn account:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and the following output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker account\n0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nDone in 21.63s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "export that address to your shell:" + } + ] + }, + { + "code": "export PRIMARY_ADDRESS=0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To get the latest block hash at any time, run:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "for an output like:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker block-latest\nBlock Number: 12783\nBlock Hash: 0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\nDone in 21.44s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll configure MetaMask." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "MetaMask" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Open MetaMask in your browser:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Add Network\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Scroll to the bottow and click \"Add Network Manually\"" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Put in this information:" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you see the error above \"This URL is currently used by the Localhost 8545 Network\", change `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "localhost" + }, + { + "type": "span", + "value": "` to `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "127.0.0.1" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We will come back to MetaMask later and complete this process; for now, copy your new address" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and export it for later:" + } + ] + }, + { + "code": "export RECIPIENT_ADDRESS=0x988a070c97D33a9Dfcc134df5628b77e8B5214ad", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "GraphQL" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Head on over to " + }, + { + "url": "http://localhost:3002/graphql", + "meta": [ + { + "id": "rel", + "value": "nofollow" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "http://localhost:3002/graphql" + } + ] + }, + { + "type": "span", + "value": " and paste the following (but with your variables):" + } + ] + }, + { + "code": "query {\n name(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n symbol(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n\n totalSupply(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "then click \"Run\" and you'll see a response like this:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"name\": {\n \"value\": \"Gold\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzavwb52aq6smf6movgcimvuoggp3cifayb2vyidg3ar546pwtb3dea\\\",\\\"ipldBlock\\\":\\\"0xf843a032575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85ba1a0476f6c6400000000000000000000000000000000000000000000000000000008\\\"}}}]\"\n }\n },\n \"symbol\": {\n \"value\": \"GLD\",\n \"proof\": {\n \"data\": \"[{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzanp5bxcn6wqd2yptbbwo5o4rx3mhpji43yd7sfd42suq6hjuhuroq\\\",\\\"ipldBlock\\\":\\\"0xf843a03a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19ba1a0474c440000000000000000000000000000000000000000000000000000000006\\\"}}}]\"\n }\n },\n \"totalSupply\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzasfla7wzuessejihdtrxqd5lqxc57egukbbricizz2ssrltex4uvq\\\",\\\"ipldBlock\\\":\\\"0xeca0305787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace8a893635c9adc5dea00000\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here's what it'll look like in the browser:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A lot has happened thus far, so let's review; we've:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "downloaded the core repos, built their docker images, and launched a local network (all using stack orchestrator)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "deployed an ERC20 token, added it to our MetaMask account" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "used the GraphQL playground to query the ERC20 watcher" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "exported a handful of shell variables which are about to come in handy" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Next we'll use the playground to query account balances:" + } + ] + }, + { + "code": "query {\n fromBalanceOf: balanceOf(\n # latest block hash\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # primary account having all the balance initially\n # created by stack orchestrator\n owner: \"0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\"\n ) {\n value\n proof {\n data\n }\n }\n toBalanceOf: balanceOf(\n blockHash: \"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\"\n token: \"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\"\n # address copied from MetaMask, has no balance initially\n owner: \"0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\"\n ) {\n value\n proof {\n data\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "the primary address should have " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " 1000000000000000000000 and the recipient address should have 0:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"1000000000000000000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzarsopkngjoijjyktfhgckq7te4dsk25gfyj653uxu4kcwqoyuykiq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5dea00000\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"0\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xb7b4b65dd5fe3800a6c38cb8a26249bbb82041d7e0b347a853b73efc7a473b75\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"\\\",\\\"ipldBlock\\\":\\\"0x\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note also that the recipient address also does not yet have a `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "cid" + }, + { + "type": "span", + "value": "` or `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ipldBlock" + }, + { + "type": "span", + "value": "`, which makes sense; this is a random account we just created and hasn't received any transactions. The network does not know about it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send it some GLD!" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn token:transfer:docker --token $TOKEN_ADDRESS --to $RECIPIENT_ADDRESS --amount 100000000 ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You'll see a familiar output:" + } + ] + }, + { + "code": "yarn run v1.22.19\n$ hardhat --network docker token-transfer --token 0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550 --to 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad --amount 100000000\nNothing to compile\nTransfer Event at block: 13371 0x412dbc25599773bfe929c67882e4a001b9d1b3b8e1c60ad4a495d5306608c77a\nfrom: 0x33AF7AB219be47367dfa5A3739e6B9CA1c40cDC8\nto: 0x988a070c97D33a9Dfcc134df5628b77e8B5214ad\nvalue: 100000000\nDone in 26.12s.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now get the latest block hash:" + } + ] + }, + { + "code": "docker exec $CONTAINER_ID yarn block:latest:docker ", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and go back to the GraphQL playground. If you've changed nothing since the last query, update only the latest block hash and run the query again, you'll see the updated account balances:" + } + ] + }, + { + "code": "{\n \"data\": {\n \"fromBalanceOf\": {\n \"value\": \"999999999999900000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzahg5shtf2rr7pompx7yx6r22zu4lea7ftlnbqkqcabvndsrvoljhq\\\",\\\"ipldBlock\\\":\\\"0xeca031bcbb6b5a44c97488b8904a85b2396b1cf337ff3ee4efb0ea1ef5104325dbfc8a893635c9adc5d8aa1f00\\\"}}}\"\n }\n },\n \"toBalanceOf\": {\n \"value\": \"100000000\",\n \"proof\": {\n \"data\": \"{\\\"blockHash\\\":\\\"0xec173c3aac86a533710569340a4ffb3f3e2d46080e35b034e93ec0049cc12174\\\",\\\"account\\\":{\\\"address\\\":\\\"0x0Dcb65938A483547835e2ebB4FC6cBf7AEe77550\\\",\\\"storage\\\":{\\\"cid\\\":\\\"bagmacgzas6xotntgq4u3v4eui6pmtbyttgikzmu7mppknam2wrekhoynupjq\\\",\\\"ipldBlock\\\":\\\"0xe7a03305adb1a8efab310b21e03d5a9f08d8cf98815372c2c4d8068e1359b8f996bc858405f5e100\\\"}}}\"\n }\n }\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Great, you've now used a watcher to see query token balances." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's send some tokens from the MetaMask recipient account back to the primary account." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Recall that when adding the network to MetaMask, we used the currency symbol \"GLD\". However, this does not mean that MetaMask can auto-detect the token, therefore, we will have to manually import it:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Copy the `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TOKEN_ADDRESS" + }, + { + "type": "span", + "value": "` and paste it in the popup. The two other fields should auto-complete:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Click \"Import Token\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and now you'll see your balance. Ignore the GLD token from earlier." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, send some tokens back to the primary address using MetaMask:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Make the gas price `" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "0" + }, + { + "type": "span", + "value": "`:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Grab the latest block hash (again) and fire off the GraphQL query for account balances to see the change." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Voila! You've successfully stood up the core Laconic stack, deployed an ERC20 token, and queried account balances." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cleanup" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tear down your docker containers with:" + } + ] + }, + { + "code": "./laconic-so deploy-system --stack erc20 down", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Next steps" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Try out the " + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator/tree/main/app/data/stacks/erc721", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "ERC721 demo" + } + ] + } + ] + } + ] + } + } + }, + "featured": false, + "id": "64023526", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/how-laconic-different.json b/json/site_content/blogPost/how-laconic-different.json new file mode 100644 index 0000000..2640d24 --- /dev/null +++ b/json/site_content/blogPost/how-laconic-different.json @@ -0,0 +1,582 @@ +{ + "data": { + "blogPost": { + "slug": "how-laconic-different", + "title": "How is Laconic different?", + "date": "2022-07-26", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/how-laconic-different.png" + }, + "content": { + "blocks": [ + { + "id": "38579992", + "text": "Laconic is a new indexing and querying solution that aims to make verifiable blockchain data available from a truly decentralized and massively scalable network. We are not the first project to address this problem, and this article will describe how our approach sets us apart from the work that has been done before." + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "item": "38579992", + "type": "block" + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Vision" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We believe that Web3 and blockchain technologies offer the promise of " + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "permissionless and equal access" + }, + { + "type": "span", + "value": " to new digital tools and financial instruments, and that these properties can be a force for positive change. Critically, decentralization of the networks and resources in Web3 is the key protection against the monopolistic jurisdiction of large tech companies and overzealous governments.  " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We further believe that the creation of public, consensus driven, cryptographically verifiable data leads to greater transparency and accountability. Finally, we believe that transacting on blockchains fosters incentive alignments through tokenomic engineering, allowing us to be more deterministic about how systems and society run." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These are the core values that imbue all of the design decisions in creating the Laconic Network. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The challenges facing Web3 adoption are significant, and threaten to prevent the fulfillment of the promise and vision. Laconic Network is focusing on a set of challenges that arise when DApp developers need to interact with blockchains and query the data that is stored there. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Working with blockchain data is more challenging than traditional web development because of the storage constraints, the overhead of preserving consensus and protecting networks from attack. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Other significant challenges include: " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Blockchain nodes are designed to be write-optimized and bear large overhead costs for storage, network gossip, and computation required for consensus and security." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "True internet scale growth (eg. comparable to Facebook or Twitter) is not possible on existing Web3 backend infrastructure. Mass adoption will require the development of a data scalability layer that facilitates for Web3 the types of usage patterns that we expect from Instagram, Google, and LinkedIn." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These challenges all compound when cross chain data is called for, which is increasingly the case. The data scalability layer will also need to account for cross-chain interoperability." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic isn’t the first to address the challenge of building a data availability layer for Web3. Companies such as Infura and Alchemy have long offered blockchain data services as convenient APIs for DApp developers, and have thus facilitated the early growth of high utility applications that interact with the underlying chains. As centralized services with quickly growing traction and clout, they already in some ways resemble the monopolistic centralized players of Web2. The convenience they offer is paid for at the price of losing censorship resistance and permissionless access. In short, we trust these companies to tell us the truth and treat us right. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Somewhat truer to the ethos of blockchain and Web3 are The Graph and Covalent, both of which have recently launched products that have elements of decentralization and verification of the data being served. For Ethereum, these services are limited to indexing events, transactions, and internal transactions, and don’t typically index the state and storage trees at all. One consequence of this is that they don’t support the Ethereum JSON RPC get_proof() call, which is essential to safeguarding the verifiability of the data being queried. No proof, no trustworthy data. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Why will Laconic’s solution solve current challenges?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network is innovating on three separate fronts: technology, governance, and incentive alignment. The challenge of serving verifiable blockchain data, at scale, from a decentralized platform is so enormous that a purely technical solution won’t suffice. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Technology" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "At a technology level, we have pioneered multiple innovations. " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD structures to preserve the hash-linked data format that is native to blockchain data—even withstanding transformations." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The concept of the Laconic Full Index Node which removes the need to re-index every time a new data access use case is materialized. The result is significantly faster setup times for customized data access API endpoints, that are tailored to individual DApps." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Re-indexing of blockchain data into specialized caches (Watchers) to simplify anticipated DApp use cases. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Content-addressability of blockchain data. In the same way that IPFS is used to request files by content hashes, blockchain data will be requested by hashes of its content, and served through the off-chain p2p IPFS network. " + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "This leads to a global hyperscale caching layer that is sufficient enough to ensure the viability of Facebook or Google level adoption of Web3." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Working with multiple international legal teams, Laconic Network is pioneering a novel governance model with the aims of ensuring unprecedented decentralization, fairness, and equality. The goals are simple: maintain a decentralized network that resists being monopolized and which is not subject to the whims of any single entity’s jurisprudence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In order to achieve those goals, a number of problems must be addressed:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A sufficient number of Validators and Service Providers to scale as Web3 grows" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A rich diversity of underlying network and data center dependencies (e.g. we aim to avoid ending up with a preponderance of machines running on AWS)" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A diversity of jurisdictional locations for network Members" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A diversity of funding sources for network members" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All governance decisions occur on-chain" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All members have equal voting power" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "No early token allocations for VCs or founders" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Incentive Alignment" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the prime benefits of blockchains is the ability to prescribe token incentives that align with desirable behaviors. For example, validators are incentivized to do the work needed to secure the network and establish consensus about block generation. The Laconic Network has created a number of incentives to align the many roles involved towards an ever more efficient and growing data availability layer for Web3." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For data consumers, it’s important to have a true selection of Service Providers (node operators who run Watchers). Since Service Providers must compete with each other on the basis of price and service level, this benefits consumers by guaranteeing the best level of service for the lowest price." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Developers writing Watchers (to be run by Service Providers), are incentivized to create useful Watchers. There is a mechanism for rewarding developers when people choose to run and query the Watchers they have written. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Members have a strong incentive to grow the network to meet demand through the fulfillment of new member auctions. These auctions bring new liquidity to the network as well as new members who can increase network capacity by indexing and serving data. A bonding curve rewards existing members with an auction fee with the earliest members benefiting the most. However, there is a disincentive to growing the network too quickly, as earnings for Members will come from indexing and serving data. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Anyone interested in building out the network will have a token incentive and opportunity to participate simply by running an in-browser cache of the off-chain data. By caching the data in this way, it will form an infinitely scalable global hyper-cache of the data, and the service level of DApps will increase as a result. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "value": "Conclusion" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic aims to significantly contribute to the growth and adoption of Web3 and blockchain technologies by solving foundational problems that currently hamper the development of DApps. This involves indexing blockchain data and making it available from a decentralized network of service providers, and doing so in a way that retains the cryptographic verifiability of that data. The innovations that allow us to do this emanate not only from technological advances, but also from the novel governance and incentivisation structures that are built into the very network structure. " + } + ] + } + ] + } + } + }, + "featured": true, + "id": "35743439", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/how-laconic-improves-the-nft-experience.json b/json/site_content/blogPost/how-laconic-improves-the-nft-experience.json new file mode 100644 index 0000000..0a9c1d1 --- /dev/null +++ b/json/site_content/blogPost/how-laconic-improves-the-nft-experience.json @@ -0,0 +1,727 @@ +{ + "data": { + "blogPost": { + "slug": "how-laconic-improves-the-nft-experience", + "title": "How Laconic Radically Improves the NFT Experience", + "date": "2022-10-05", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "/images/site_content/blogPost/how-laconic-improves-the-nft-experience.png" + }, + "content": { + "blocks": [ + { + "id": "55708950", + "label": "Discord", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Part 1 of this two-part series discussed the five major implementation and integration issues plaguing DApp developers, along with an overview of how Laconic addresses each one:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "1. NFT data lacks consistency and integrity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "2. NFT data is fragmented and scattered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "3. For both NFTs and equivalent token formats, data types vary from blockchain to blockchain." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "4. NFT games require lightning-fast retrieval and scalability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "5. Data retrieved from multiple locations is difficult to verify. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now, in Part 2, we'll explore how these problems are playing out in the real world—and how Laconic tools and processes shield both users and developers from the effects of shifting and conflicting standards, radically reducing everyone's list of problems.   " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Apes, witches ... tax fraud? The power of caching." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let's start by looking at a current cornerstone of the industry. Arguably the most popular use case for NFT artwork is " + }, + { + "url": "https://boredapeyachtclub.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "10k PFP collections" + } + ] + }, + { + "type": "span", + "value": ", collections of (typically 10,000) relatively simple anthropomorphized cartoon jpegs, generated by combining variations on a large but limited set of defined metadata “traits,” each of which correlates to a physical attribute of the generated character drawing. " + }, + { + "url": "https://boredapeyachtclub.com/#/home", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Bored Ape Yacht Club" + } + ] + }, + { + "type": "span", + "value": ", for example, uses this model. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the case of another popular collection, " + }, + { + "url": "https://www.cryptocoven.xyz/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Crypto Coven" + } + ] + }, + { + "type": "span", + "value": ", a smart contract is called to mint tokens based on a similar list of randomly generated traits. To gather each piece of this data and display it, a wallet client or platform makes an API call to an " + }, + { + "url": "https://docs.metamask.io/guide/rpc-api.html#table-of-contents", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "RPC" + } + ] + }, + { + "type": "span", + "value": " provider. To retrieve the data, the client or platform must sift through an extraordinary amount of data to find just a few essential pieces. And all that searching eventually adds up. More serious problems could also arise, from sandwich MEVs to lowball ape sales as a " + }, + { + "url": "https://www.cryptonews.net/news/nft/6583225/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "front for tax shenanigans" + } + ] + }, + { + "type": "span", + "value": ".\n\nLuckily, there's an easier (and less easily abused) option. A Laconic Watcher could cache all data related to a given smart contract—in this case, " + }, + { + "url": "https://cryptocoven.mirror.xyz/A622VSRm8-9oLzc8l3oFGmfnFUZQmDQ3Wx3ObhSlhsc", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the brilliant Crypto Coven `counters.sol` variant" + } + ] + }, + { + "type": "span", + "value": "—making it instantly available at any DApp request while preserving proof of authenticity. Because Laconic can prove metadata at the current block height, not several blocks behind like most current centralized data providers, all NFT data received is guaranteed correct. That makes for faster queries, lower costs, easier updates—a quality-of-life upgrade for developers. For a particularly busy smart contract such as Bored Ape Yacht Club, Laconic's data cache could also drastically improve the daily experience of thousands of users. And let's not forget the damage done to industry reputation by " + }, + { + "url": "https://nftevening.com/bored-ape-6462-sold-for-200/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the successful exploits of every \"fat-fingered\" Ape owner" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Where we’re going, we don’t need standards." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Smart contracts are incredibly popular for good reason. But there are " + }, + { + "url": "https://etherscan.io/address/0x53e4c0167ed855e96f562dbb911854d586f5cc07#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "countless" + } + ] + }, + { + "type": "span", + "value": " " + }, + { + "url": "https://etherscan.io/address/0x517e643f53eb3622fd2c3a12c6bfde5e7bc8d5ca#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "variations" + } + ] + }, + { + "type": "span", + "value": " out there, even on Ethereum. In fact, " + }, + { + "url": "https://www.smartcontractresearch.org/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "entire Web2 and Web3 developer communities" + } + ] + }, + { + "type": "span", + "value": " view this aspect of NFT technology as where its real beauty is revealed: the developer as artist, Making It New. Limit contract and metadata standards, the argument goes, and you'll hamstring developers' creativity, starving the ecosystem of innovative ideas. (Unaddressed standards are, of course, nothing new. Think back to the browser wars of Web 1.0, when entire websites ... just didn't work on Internet Explorer, and at many design shops, ensuring IE functionality would cost you a good 20% extra.)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is uniquely suited to solve the problems associated with nonstandardized token data retrieval. By caching token data into microchains of IPLD blocks, and serving it directly to DApps from the resulting decentralized content-addressable database in a unified format, the Laconic Watcher makes the entire issue moot. It displays the precise data queried, confirms that it's correct—and does so more quickly and with a more current state than any standard RPC service." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "An end run around fragmented and scattered metadata." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A Laconic Watcher makes it easy for developers to retrieve blockchain data from an indexed database; fetch off-chain data as needed; and correlate, merkleize, and cache the results. With a " + }, + { + "url": "https://etherscan.io/address/0xe6ddda1c3f1cb01aa5c86a21e8636deabfd1f013#code", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Crypto Coven smart contract" + } + ] + }, + { + "type": "span", + "value": ", for example, there's no need to concern yourself with both the smart contract and the media the tokenURI points to—a Watcher can tie them together in ways standard RPC providers can't, with a terrific signal-to-noise ratio, perfectly up-to-date state, and persistent uptime.\n\nWatchers define and expose precisely crafted APIs for DApps that consume specific data sets, such as a given smart contract. Allowing a DApp itself to focus solely on its primary mission of delivering the goods to the user eliminates the need for (often surprisingly heavy) query lifts." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Smooth token data retrieval and management, even from multiple blockchains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Non-fungible tokens exist on multiple blockchains, in varying degrees of complexity, in a range of programming languages, in line with wildly varying (if any) metadata standards. The result is, unsurprisingly, significant friction when it comes to token management. Laconic removes much of the drag by unifying and extending the possibilities of metadata once it’s been retrieved." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To maintain a unified format regardless of chain or data source, Laconic stores all blockchain data in content-addressable data formats. This allows for a level of global availability and extensibility that makes DApp development far more convenient. " + }, + { + "url": "https://www.laconic.com/blog/99-problems-but-nfts-aint-one", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Our hypothetical on-chain Library of Alexandria in Part 1" + } + ] + }, + { + "type": "span", + "value": " is made possible by Laconic’s uniquely decentralized, content-addressable format and the Watcher's computationally light footprint. Think of this chain-agnostic view as a Rosetta Stone for Web3 data." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic makes querying NFT data verifiable, fast, and affordable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic makes DApp access both faster and easier with its up-to-date, indexed, verifiable blockchain data and custom query and caching services; queries, too, become more affordable to develop and maintain. And in a time of ballooning NFT data stores and rapidly increasing demands on user experience, Laconic Watchers deliver data at a fraction of the cost of traditional data retrieval." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In short, the architecture of the Laconic Network delivers practical solutions to many of today's most pressing NFT challenges. Custom Laconic Watcher services can, for example:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collect data from multiple blockchains" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Process that data to make it consumable by DApps" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Preserve data verifiability across transformations " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Keep data up to date" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Streamline DApp data retrieval" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With these capabilities, the Laconic Network becomes an indispensable data querying and verification layer for any NFT-related service—and, more than any other approach to querying blockchain data, a resource with the capacity to help the entire industry thrive." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Read more about Watchers and the Laconic Network " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "here" + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Got questions? Join us on Discord." + } + ] + }, + { + "item": "55708950", + "type": "block" + } + ] + } + } + }, + "featured": true, + "id": "55708951", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/how-laconic-network-uses-ipld.json b/json/site_content/blogPost/how-laconic-network-uses-ipld.json new file mode 100644 index 0000000..01c3ae5 --- /dev/null +++ b/json/site_content/blogPost/how-laconic-network-uses-ipld.json @@ -0,0 +1,609 @@ +{ + "data": { + "blogPost": { + "slug": "how-laconic-network-uses-ipld", + "title": "How Laconic Network Uses IPLD", + "date": "2022-08-30", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "7023106", + "name": "Christoph Berger" + }, + "image": { + "url": "/images/site_content/blogPost/how-laconic-network-uses-ipld.png" + }, + "content": { + "blocks": [ + { + "id": "55419640", + "title": "IPLD" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Accessing blockchain data off-chain is no longer a matter of trust" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The mission of the Laconic Network is to ensure that decentralized off-chain caches can serve blockchain data without losing the ability to prove the integrity of that data across data transformations. This post shows how the Laconic Stack maintains the verifiability of Ethereum data for its decentralized caching and querying solution. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Querying blockchain data is difficult and expensive" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "DApp developers face a dilemma when it comes to working with blockchain data. Ethereum data is stored in various ways, including the state, storage, and transaction tries, as well as in event logs. The original concept for accessing Ethereum data was that DApps would run or have access to an Ethereum full node and query it directly. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "However, DApp developers rarely want to run their own full node only to gain access to the data they need. Further, and more problematic, due to the way data is stored on Ethereum, there is often no direct way of querying for particular information. Because blockchains are optimized for writing new blocks and achieving consensus, they cannot be indexed like a classic database. As a result, to query for data, developers have to make multiple calls to the Ethereum API, replay transactions to read the events emitted, and store intermittent state in the application to join it with data from subsequent invocations.  This process is difficult and expensive." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The alternative to running a full node is to use a middleware indexing and caching service. While this alternative simplifies data acquisition for developers, it introduces some new and significant problems. In addition to high costs, long setup times, reliability issues, and the dependence on Web2-style centralized services, there is one problem that is especially pernicious: the data served by these services must be " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "trusted" + }, + { + "type": "span", + "value": ". It is currently impossible for existing centralized data providers to offer cryptographic proofs for every byte of data served." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Solution: Laconic Network and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Many people are familiar with the Interplanetary File System (IPFS), but fewer people know about the Interplanetary Linked Data (IPLD) protocol. IPLD is the underlying technology of IPFS, and it is the superpower technology behind many of IPFS’s advantages. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD can represent arbitrary data in self-describing structured objects, and it can link those objects into Merkle DAGs. As such, IPLD offers the following advantages:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is content-addressable via a Content ID (CID), just as IPFS files are content-addressable." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is cryptographically provable, meaning you can verify that a set of data belongs in a larger data set even without needing to have either set on hand. You only need the cryptographic hashes." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data undergoes natural deduplication, reducing storage and transport costs." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data is tamper-proof. Any attempts to change the data that you have requested via content addressing can be foiled by validating the hashes." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "IPLD codecs map data from its original form to the IPLD data model. So if a codec exists for that data, the data has a formal representation in the IPLD data model. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Connection to Ethereum" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the case of Ethereum data, the " + }, + { + "url": "https://ipld.io/specs/codecs/dag-eth/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "DAG-ETH codecs" + } + ] + }, + { + "type": "span", + "value": " can be used to map on-chain data to off-chain IPLD structures. This mapping to IPLD makes it possible to inspect, process, or reason about data in a uniform way. The codecs cover block headers, uncles, transactions, transaction receipts, and receipt logs, as well as all the different Merkle Patricia tries that are rooted in an Ethereum block. As a result, IPLD can accurately represent any Ethereum data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike the original data, the off-chain IPLD representation enables indexing for fast querying. The restrictions of the blockchain do not apply here. Indexes can be added that allow developers to query data in ways that are otherwise difficult to achieve with the native ETH JSON RPC API. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "More importantly, the IPLD representation of blockchain data can be kept verifiable even after transforming the data into other models. Transformations are necessary to support complex DApps that aggregate and link data in app-specific ways or rely on a non-native representation of the data. The classic methods of slicing, dicing, and recombining the data would render it unverifiable. With Laconic Network data, thanks to our use of IPLD, the data remains as verifiable as it is on the blockchain." + } + ] + }, + { + "item": "55419640", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Merkleized data is provable data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The IPLD representation of an Ethereum block includes the block header and the uncles, transactions, receipts, logs, receipt trie, transaction trie, state trie, and storage tries that Ethereum stores alongside the block header. A modified Merkle Patricia Trie (MMPT), which is relevant to Ethereum, has branch nodes, extension nodes, and leaf nodes. Non-leaf nodes store the content hashes of their child nodes. This hashing goes all the way up to the root node. Therefore, the hash value in the tree root represents all leaf data. If any part of the data in a Merkle tree is tampered with, the root hash would change and differ from the root hash stored in the block header. In other words, data like state, transactions, and more, is connected to the block header in a provable way.  " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Storing all of the IPLD: Laconic Full Index Nodes" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Traditionally, Ethereum Full Nodes are used to power DApps that need to access and query all of the data of the blockchain. The disadvantages of relying on a normal Full Node are well known: " + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Expensive to operate" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not optimized for data querying" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Logs stored from events have to be replayed to access the data" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Full Index Nodes (LFIN) overcome these limitations by generating additional derivative data based on that which is stored in the Ethereum Full Node, adding new indexes, relational mappings, and materialized views, and even merkleizing some data that was previously not merkleized. The result is an amount of data that far exceeds what is normally stored. The role of the LFIN is to track and store the complete data, including derived indexes and Merkle trees of that data, in IPLD block format, for eventual consumption by DApps. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data representation inside of the LFIN optimizes many classes of queries by removing the need  to chain together data from multiple transactions. For example, listing all transactions for a particular wallet address could be achieved with a single query. Or gathering all of the node hashes needed to generate a cryptographic proof. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Laconic Watchers - interoperable and provable blockchain data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because the volume of data in the Laconic Full Index Node is so vast, most DApps would not want to have to query that database directly. Instead, DApps work with a specialized caching layer that exists to transform LFIN data into the format needed by specific DApps. These are Laconic Watchers. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The data in the LFIN is still not ready for DApp developers at this point. Further transformation is needed to structure the data in the format required by your DApp. This is the role of the Laconic Watcher. Watchers will be the topic of a subsequent article, but within the scope of this article, you can understand them as being custom secondary or tertiary caches of data that directly fulfill the needs of specific applications. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thanks to our use of IPLD, these transformations can occur without losing the cryptographic integrity of the data. Developers can build their Watchers to track, process, and cache the underlying blockchain data, and then query the watchers using GraphQL. The returned data includes the linked hash structure that can be followed all the way back to the underlying blockchain. In the Laconic Network, when any data is being transformed, the hash of the inputs to the transformation and the hash of the code that performs the transformation are preserved. Thus:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "code" + ], + "value": "T(a+b) => c" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The new model, c, will contain content-hash references to a, b, and the code performing T." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One intentional consequence of our adoption of structured IPLD models for data representation is that it greatly simplifies dealing with cross chain data. Our ability to transform data while maintaining linked hashing will be invaluable when we begin to index chains beyond Ethereum. Laconic Network is designed with blockchain interoperability in mind." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The source of proof" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network improves the Web3/blockchain ecosystem greatly by combining the validation-preserving nature of IPLD with a new way of caching and transforming blockchain data in a decentralized manner. This ability frees DApp developers from relying on centralized, trust-based data providers and closes the cryptographic provability gap between the blockchain and DApps built to use blockchain data. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Stay up-to-date with Laconic news:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://discord.com/invite/ukhbBemyxY", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Join our Discord server" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://t.me/share/url?url=https%3A%2F%2Fwww.laconic.com%2Fblog%2Fhow-laconic-network-uses-ipld", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Subscribe to our Telegram channel" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Sign up for our mailing list below" + } + ] + } + ] + } + ] + } + ] + } + } + }, + "featured": true, + "id": "43258780", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/intro-to-the-laconic-stack.json b/json/site_content/blogPost/intro-to-the-laconic-stack.json new file mode 100644 index 0000000..d31e6aa --- /dev/null +++ b/json/site_content/blogPost/intro-to-the-laconic-stack.json @@ -0,0 +1,604 @@ +{ + "data": { + "blogPost": { + "slug": "intro-to-the-laconic-stack", + "title": "Intro to the Laconic Stack", + "date": "2023-01-18", + "category": [ + { + "id": "6311820", + "slug": "developers", + "title": "Developers" + } + ], + "author": { + "id": "63992470", + "name": "Zach Ramsay" + }, + "image": { + "url": "/images/site_content/blogPost/intro-to-the-laconic-stack.png" + }, + "content": { + "blocks": [ + { + "id": "63992474", + "title": "Laconic Stack Diagram" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Here’s the main problem: reading data from the Ethereum blockchains is either cheap and sloppy or expensive and correct. As a result, Dapp developers have come to rely on inexpensive centralized services that do not provide evidence to verify the correctness of the data they are serving to Dapps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only is it expensive to get verifiable data but it can also be challenging to parse out the subset of data you really need. In the early days of SQL, you had to be proficient at the command line in order to use the product, and so use was limited to those that had that specialized capability." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Eventually, GUIs were built such that anyone with basic computer skills could use drop-down menus and create database schemas without writing a single line of code. Web3 is still in the early days of SQL, it is not easy onboarding new users and developers who are otherwise quite capable with the latest Web2 technologies.\n\nRight now, there’s all this data on Ethereum and as a Dapp developer, you only want a tiny fraction of it. But, to verify that fraction, you have to (among several other things) maintain an archive node - this is prohibitively expensive for the majority of developers. To solve this problem, centralized services such as (Infura, The Graph, and Alchemy) have popped up and currently account for the majority (if not most) of Dapp queries to Ethereum." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic was created to address these and several other problems in the blockchain ecosystem. Not only does Laconic make it easy to get verifiable data - quickly and cheaply - it also provides a framework for data transformation and aggregation that are difficult or impossible to do in other systems.\n\nArchitecting a solution to this requires many moving pieces; these have been developed by core Ethereum & Cosmos contributors over the past 5 years. In this post, we will walk you through the various components of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are three different ways to participate in the Laconic Network: Member Validators, Service Providers, and Dapp Developers. To describe the responsibilities and benefits of each role, we must first start grounded in the technicalities of the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Let’s take a look at the following core stack diagram:" + } + ] + }, + { + "item": "63992474", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note: this diagram intentionally leaves out several repositories (e.g., codecs, utilities, rpc shims). This is done for simplicity reasons and anyone diving deep into the stack will discover them.\n\nThe two repositories at the top are also the main entry points for most developers. `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` is a command-line tool for, well, orchestrating the stack. It uses docker-compose to deploy a specified collection of networked docker containers, thereby eliminating the need to set up a variety of services independently. Every user of the Laconic Stack will at some point - if not regularly - use the stack orchestrator." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watchers-ts" + } + ] + }, + { + "type": "span", + "value": "` repo contains the publically available Watchers and the code to generate them. Watchers are TypeScript that is generated from one or more Solidity smart contracts. Dapp Developers can participate in the Laconic Network by either 1) writing a custom watcher for their Dapp or 2) writing a generally useful watcher and publishing it to the Laconic Registry, thus earning a fee every time it is used. We’ll come back to Watchers later." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Down at the bottom left is the `" + }, + { + "url": "https://github.com/cerc-io/laconicd", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconicd" + } + ] + }, + { + "type": "span", + "value": "` repository and it is indeed the “bottom” of the stack. It is built from the Cosmos SDK and has custom modules specific to operating the Laconic Network (e.g., fork of Ethermint/Evmos, auction, nameservice). It is likely that in the future there will be a public testnet, however, because the Laconic Network is a permissioned validator set, only Member Validators that have officially joined the Laconic Network will be included in the mainnet. Just because the validator set is permissioned does not prohibit anyone from running a full node and Service Providers or others may choose to do so for a variety of reasons." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/laconic-sdk", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-sdk" + } + ] + }, + { + "type": "span", + "value": "` is a library for facilitating talking to `laconicd`. Both the `" + }, + { + "url": "https://github.com/cerc-io/laconic-registry-cli", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-registry-cli" + } + ] + }, + { + "type": "span", + "value": "` and the `" + }, + { + "url": "https://github.com/cerc-io/laconic-console", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "laconic-console" + } + ] + }, + { + "type": "span", + "value": "` use it. While `laconic-registry-cli` is a command-line tool for doing so, the `laconic-console` is a user interface for writing and reading records on the Laconic Network. These general-purpose tools are useful for a wide variety of use cases across the stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A key goal of the Laconic Network is to provide accurate, verifiable data from the Ethereum blockchain. Comparisons to currently available solutions are for another post, however, no service currently exists to provide inexpensive evidence that the data being served is correct. The Laconic solution (one of) to this is in something called “statediffing”, a part of the stack run by Member Validators and, likely by Service Providers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It starts with a maintained fork of `" + }, + { + "url": "https://github.com/cerc-io/go-ethereum", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "go-ethereum (geth)" + } + ] + }, + { + "type": "span", + "value": "` that has an added real-time state-diffing service. Statediffing gives a clear picture of the state between any given blockheights. This allows Laconic to minimize the amount of computation required for providing proofs. Three additional “helper” services perform different tasks required to get a full picture of the state as required by an application. Together, these comprise the Full Index Node (FIN)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-service" + } + ] + }, + { + "type": "span", + "value": "` provides historical state data, while the `" + }, + { + "url": "https://github.com/cerc-io/eth-statediff-fill-service", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "eth-statediff-fill-service" + } + ] + }, + { + "type": "span", + "value": "` uses the historical state data to fill statediff gaps as required. Finally, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-state-snapshot", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-state-snapshot" + } + ] + }, + { + "type": "span", + "value": "` loads a complete state at a certain blockheight, which helps to bootstrap the system. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nThere is more to be written about statediffing, however, what’s important to note here is that each service is writing independently to `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-db", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-db" + } + ] + }, + { + "type": "span", + "value": "`. The latter serves as a bucket for state data that has been indexed in IPLD. Rather than querying this database directly, the `" + }, + { + "url": "https://github.com/cerc-io/ipld-eth-server", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "ipld-eth-server" + } + ] + }, + { + "type": "span", + "value": "` provides an API layer for Watchers to easily query relevant pieces of data from the Ethereum state. Additionally, `ipld-eth-server` recapitulates the native Ethereum JSON RPC interfaces on top of the `ipld-eth-db` database." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "And so we’ve come full circle back to the Watchers. As previously mentioned, they are generated from one or more Solidity smart contracts and configured to query specific pieces of data relevant to a Dapp. Watchers make it easy to query the data you need from Ethereum " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "and" + }, + { + "type": "span", + "value": " - along the way - get evidence in order to generate proofs that your data is correct. This is in contrast to currently available solutions for Dapp developers, who must currently rely on centralized providers that don’t provide evidence to generate proofs." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Web3 is (still!) in its early days, and like the early days of SQL, Dapp developers need advanced knowledge of complex data structures to build their Dapp. Watchers simplify this by exposing a GraphQL endpoint, a solution familiar to an order of magnitude more developers than querying the Ethereum blockchain directly." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is building a suite of tools to address core problems in Web3. Today, we’ve provided an overview of the main components of the Laconic Stack. Developers interested in Laconic should start with `" + }, + { + "url": "https://github.com/cerc-io/stack-orchestrator", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "stack-orchestrator" + } + ] + }, + { + "type": "span", + "value": "` to get a sense of running different parts of the stack, then check out `" + }, + { + "url": "https://github.com/cerc-io/watcher-ts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "watcher-ts" + } + ] + }, + { + "type": "span", + "value": "` to experiment with different watchers and progress to making their own." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "63992475", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/introducing-laconic-network.json b/json/site_content/blogPost/introducing-laconic-network.json new file mode 100644 index 0000000..290101e --- /dev/null +++ b/json/site_content/blogPost/introducing-laconic-network.json @@ -0,0 +1,746 @@ +{ + "data": { + "blogPost": { + "slug": "introducing-laconic-network", + "title": "Introducing Laconic Network", + "date": "2022-07-26", + "category": [ + { + "id": "2965426", + "slug": "news", + "title": "News" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/introducing-laconic-network.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The advent of smart-contract blockchains, led by Ethereum in 2014, has given rise to exciting new possibilities for humans interacting and transacting with each other. The ensuing rise of Web3, with wider consumer adoption of DeFi and NFTs in particular, has begun to show the world how public blockchains that are permissionless and trustless can bring immense value to industries and individuals. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The value of blockchain:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Immutable data that brings transparency to finance and governance" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Efficiency through smart contract automation " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Security and privacy through emerging cryptography " + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Yet all of these benefits hinge on developers succeeding in writing Web3 applications on top of blockchains that are fast, efficient, secure, and frictionless to use. There will be no internet-scale adoption of Web3 if the user experience is bad, or if it is simply too difficult to write the applications in the first place. And this point highlights a critical weakness of blockchains - accessing the data that they store is not as simple and straightforward as one might expect. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the contrary, since blockchains are write-optimized databases that focus nearly exclusively on establishing consensus and finality of each new block, they are notoriously difficult to work with for data retrieval." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The result of this has been the rise of a middleware industry to index, cache, and serve blockchain data. This includes services like Infura, Alchemy, and The Graph. These services, while more convenient for developers to use when building DApps, all break the fundamental benefits of blockchain in one or more ways. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, they tend towards centralization, and we end up with an industry where the blockchains are decentralized, but the DApps built on them all pass through an oligarchy of service providers that are not materially different from the IaaS cloud providers that represent the hyper-centralization of Web2. Second, they serve data that can no longer be cryptographically proven to be the correct data, so the trustless benefit of blockchain technology is replaced with a trust relationship with a single company. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Until Laconic, there has not been any technology that serves blockchain data while truly upholding the ideals of decentralization and allowing all data to be independently cryptographically verified. Key components of the emerging Web3 ecosystem, like Metamask, use centralized APIs from OpenSea and Infura without any mechanism for cryptographically verifying the accuracy or provenance of the data. Yet, provable data and decentralization are essential to having truly trustless systems for Web3 to depend on." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It is precisely this service that Laconic has been created to deliver." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Blockchain Data for High-Performance Applications" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network solves these two fundamental and existential problems. We are creating a truly decentralized network of data indexers and API providers to deliver hash-linked (cryptographically provable) blockchain data off-chain. Every byte of data served to DApps via the Laconic Network can be verified and proven to be mathematically integral." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We are thrilled at the prospect that the Laconic Network will become the catalyst and enabler to unlock immense potential value in the blockchain. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the last five years, our team of platform experts across Ethereum, IPLD / IPFS, and Cosmos SDK has been leading research and development in this space. We are creating a new set of technologies and infrastructure to solve the fundamental problem of making blockchain data accessible to high performance, high availability applications. The work has included the application of IPLD block structures to preserve the hash-linked structure of blockchain data when stored in traditional relational databases and served via API endpoints." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "The upshot: No longer will DApp developers have to struggle with convoluted queries, poor performance, long indexing times, or unprovable data when building decentralized applications. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Enter the Laconic Network" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Furthermore, we’ve pioneered a novel organizational and governance structure that solves the seemingly incompatible goals of providing guaranteed data availability service levels via a truly decentralized network. Laconic Network Members are corporate entities who enter contractual obligations to offer data indexing and retrieval services to end-users with professional-grade performance guarantees. Yet the network of these partners is technically, legally, and even geographically decentralized, and therefore impervious to censorship or monopolistic control. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the primary responsibilities of Laconic Network Members is to run Laconic Watchers, which are programs that expose data APIs to DApps, maintaining specific views of blockchain state and data expressed as mini-blockchains, solely for the purpose of reliable, provable data querying. DApp developers can find and use existing Laconic Watchers and can programmatically create new Watchers. There will be token incentives for programmers who write Watchers. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, the Laconic Network features an incentivized data caching layer to ensure robustness.  All blockchain data can be made permanently, globally available through the caching layer, without needing to directly query blockchain nodes, and, of course, without sacrificing the cryptographic integrity of the data. People who care about decentralization and provable data will be able to participate in this caching layer and earn tokens while doing it." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The upcoming launch of the Laconic Network will bring a new era marked by the positive value that blockchains bring to humanity, with ever more practical and impactful use cases emerging as the barriers of data availability fall away." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network will have opportunities and benefits for anyone who cares about the future of the decentralized web:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://discord.com/invite/ukhbBemyxY", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Join our Discord" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "url": "https://t.me/laconicnetwork", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Chat with us on Telegram" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and " + }, + { + "url": "https://www.laconic.com/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "sign up for our Newsletter" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "How have others tried to solve these challenges?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The original vision of Ethereum was that DApp developers would connect directly with Ethereum nodes and use the JSON-RPC API of an Ethereum client to query data. This allows you to get data from a source that has verified the data, and is decentralized, but it requires you to run the node yourself, or hire someone to run it for you. Furthermore, the data is not indexed in such a way that is convenient for DApp developers, and a large number of queries and processing must be done to satisfy normal application cases." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To improve the convenience of querying Ethereum data, many indexers have taken the data and put it in off-chain databases, often with sensible indexes. They then expose this data over new APIs that foster rapid application development and developer ease, but completely forsake the first principle values of decentralization and cryptographic verifiability. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To our knowledge, no indexer has managed to provide the convenience of custom indexes and APIs while preserving decentralization and cryptographic proofs of the data integrity." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Laconic's Solution: How are we different?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Developed over four years by our team of Ethereum, IPLD, and Cosmos SDK core contributors, the Laconic Solution encompasses three main components:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Stack, a set of services which convert Ethereum data into IPLD blocks and populate our high-performance, carefully indexed databases.\n\n" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic SDK, which makes it easy for developers to write front ends and other services which consume IPLD-based data structures and proofs.\n\n" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network, a multi-sided marketplace that allows DApp developers and service providers to transact in a more cost-effective way and then pass those cost savings on to end-users, providing a sustainable and decentralized operating model for DApp developers. The key element of the Network is Watchers, custom materialized views of blockchain data that developers query to access specific blockchain data." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Stack consists of our modified Geth client which extracts and denormalizes the data, transforming it into IPLD blocks and indexing it properly for efficient querying and future transformations. For example, we can list all the node hashes needed to execute a cryptographic proof in one query, instead of having to walk down the Merkle tree with a chain of sequential queries. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The main contact point for developers is the Watcher, a materialized view of a specific subset of blockchain data (e.g. a specific contract or protocol) that provides a custom endpoint for DApps to query.  " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "$LNT, the Laconic Network Token" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With a fixed total supply, Laconic Network Token (LNT) serves multiple functions in the network design, most saliently by securing incentive alignment across network stakeholders. In addition to being used as rewards for service providers to run Watchers, LNT also allows for seamless transactions across services, regardless of blockchain or DApp." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Distribute rewards to staked users" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Provide service discounts" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Distribute funds to community members for Network maintenance and development" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "What’s Next for Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth mode, we’re looking forward to our public marketing debut this month. Later in Q3 2022, we will announce the initial cohort of seven Founding Members, as well as a funding round. As our technology, governance, and network are novel, with no current analogies, we're excited to share more articles about key aspects of the Laconic Stack and Laconic Network, including our use of IPLD, the role of Watchers, the features of the Laconic App, and our pioneering governance framework." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For those wishing to run validators, we're aiming to have an incentivized Testnet in 2023, followed by the launch of our Mainnet later in 2023." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Summary of Laconic Network Milestones" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Public Marketing Launch: Q3 2022" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Coming out of a long stealth period, we’ll be launching our marketing assets and channels, including branding, website, social media and developer platforms, and content." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Seven Founding Members and Funding: Q3 2022" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ll be announcing our initial seven Founding Members and the close of our seed funding round." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentivized Testnet: Early 2023" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With the launch of our Incentivized Testnet, prospective validators will be incentivized to learn about the requirements for being a Member/Validator on Laconic Network and how to operate the Laconic Stack." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Mainnet Launch & Incentivized Global Data Cache: Late 2023" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Once Laconic Mainnet launches, " + }, + { + "type": "span", + "value": "and 7 Member/validators have joined the network, we’ll have an incentivized global data cache, allowing anyone to easily participate in serving blockchain data to DApps (and earn $LNT in doing so)." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "24195830", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/laconic-and-consensys-metamask-launch-mobymask-light-client.json b/json/site_content/blogPost/laconic-and-consensys-metamask-launch-mobymask-light-client.json new file mode 100644 index 0000000..2e4392c --- /dev/null +++ b/json/site_content/blogPost/laconic-and-consensys-metamask-launch-mobymask-light-client.json @@ -0,0 +1,448 @@ +{ + "data": { + "blogPost": { + "slug": "laconic-and-consensys-metamask-launch-mobymask-light-client", + "title": "Laconic and ConsenSys’s MetaMask Launch MobyMask Light Client", + "date": "2022-11-08", + "category": [ + { + "id": "3545001", + "slug": "partners", + "title": "Partners" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/laconic-and-consensys-metamask-launch-mobymask-light-client.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "blockquote", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "\"It’s hard to overstate what an achievement this [Laconic] is for bringing down the computational costs of privacy, scalable data distribution, and also speed of data lookups for users who repeatedly use the same contracts but don’t necessarily run their own full nodes.\"" + } + ] + } + ], + "attribution": "Dan Finlay, MetaMask Founder" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Today I’m thrilled to announce that " + }, + { + "url": "http://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-clienthttps://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic is partnering with the world’s leading self-custodial wallet, ConsenSys’s MetaMask" + } + ] + }, + { + "type": "span", + "value": ", and MetaMask founder Dan Finlay, to launch " + }, + { + "url": "https://mobymask.com/#/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "MobyMask" + } + ] + }, + { + "type": "span", + "value": ", an anti-phishing tool. You can check out Dan Finlay’s " + }, + { + "url": "https://mirror.xyz/0x55e2780588aa5000F464f700D2676fD0a22Ee160/8whNch3m5KMzeo6g5eblcXMMplPf8UpW228cSh3nmzg", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "original MobyMask blog" + } + ] + }, + { + "type": "span", + "value": ", and his " + }, + { + "url": "https://metamask.io/news/security/meta-mask-and-laconic-launch-moby-mask-light-client", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "blog announcement today" + } + ] + }, + { + "type": "span", + "value": ", for more information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MobyMask is a community-sourced registry for managing and reporting phishing accounts across social media and Web3 intersections, based on MetaMask's " + }, + { + "url": "https://mirror.xyz/0x55e2780588aa5000F464f700D2676fD0a22Ee160/pTIrlopsSUvWAbnq1qJDNKU1pGNLP8VEn1H8DSVcvXM", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Delegatable framework" + } + ] + }, + { + "type": "span", + "value": ". It provides robust tools for sourcing phishing reporters from across online communities, using a dynamic web of trust. The registry is designed to grow organically as community members report scammers and phishers directly onto the Ethereum mainnet." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Accessible, affordable privacy and security" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic's MobyMask-caching Ethereum light client greatly reduces the cost of hosting a trustworthy copy of the MobyMask anti-phishing registry, making the tool accessible and affordable for a wide range of users. By optimizing the blockchain query process, explains Dan Finlay in his blog announcement today, Laconic \"greatly reduces the computational costs of privacy, scalable data distribution, and also speed of data lookups for users who repeatedly use the same contracts but don’t necessarily run their own full nodes.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "MobyMask further streamlines blockchain security by bringing light-client functionality directly to the browser. The key is the " + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watcher" + } + ] + }, + { + "type": "span", + "value": ", an essential piece of the Laconic Stack that caches only the specific blockchain data required for a particular query—reducing data requirements by orders of magnitude while creating a lightweight, self-hostable process that web services such as MetaMask, WalletGuard, and Phishfort can use to draw MobyMask phishing detection data. A soon-to-be-released TypeScript version of our Watcher will streamline security even further, with support for fully browser-based list caching and peer-to-peer data replication. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Lowering barriers for entry to Web3 " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Phishing and related scams plague both novice and experienced blockchain users, creating further barriers to adoption and limiting the potential for growth of the Web3 ecosystem. That’s created a real need for tools that make this ecosystem safer, faster, easier, and more affordable to use. In reducing technical requirements for interacting with the blockchain, the Laconic protocol lowers barriers to entry, making Web 3 technologies more accessible to those with limited resources." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With that goal in mind, Laconic is working on an update that lets users search a dynamic repository of phishing reports, and share them with a network of peers, via a free private API. Messages will be fully provable on chain, with blockchain used only to resolve registry conflicts and revoke access when needed. While the initial repository is by invitation only, Laconic and MobyMask plan to eventually allow users to subscribe to multiple roots of trust, and to host their own. Meanwhile, we’re laying the groundwork for an ambitious shared vision: a system with the ability to verify credible sources for a wide range of information types." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "A highly scalable and privacy-preserving application" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With MobyMask making Web3 access safer and less stressful for everyone involved, the potential benefits, says Dan, are substantial. ”If we’re going to build anything of value out of decentralized technology, we need to basically eliminate phishing. That's going to take a lot of creativity and ingenuity, and we’re happy that " + }, + { + "url": "https://www.laconic.com/", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic" + } + ] + }, + { + "type": "span", + "value": " and MobyMask combine so well to deliver a highly scalable and privacy-preserving application whose safety remains rooted on the blockchain.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\nYou can learn more about MobyMask at " + }, + { + "url": "http://www.mobymask.com", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "mobymask.com" + } + ] + }, + { + "type": "span", + "value": ". For more information on Laconic, " + }, + { + "url": "https://discord.com/invite/ukhbBemyxY", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "join the Laconic Discord" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "56055548", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/laconic-devcon-vi-recap.json b/json/site_content/blogPost/laconic-devcon-vi-recap.json new file mode 100644 index 0000000..9a5870b --- /dev/null +++ b/json/site_content/blogPost/laconic-devcon-vi-recap.json @@ -0,0 +1,330 @@ +{ + "data": { + "blogPost": { + "slug": "laconic-devcon-vi-recap", + "title": "Laconic’s Devcon VI Recap", + "date": "2022-10-20", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "/images/site_content/blogPost/laconic-devcon-vi-recap.png" + }, + "content": { + "blocks": [ + { + "id": "55844240", + "title": "MEV" + }, + { + "id": "55844241", + "title": "OFAC" + }, + { + "id": "55844702", + "title": "Regulation" + }, + { + "id": "55846103", + "title": "Devcon" + }, + { + "id": "55844242", + "label": "Join Our Discord to Learn More", + "url": "https://discord.com/invite/ukhbBemyxY" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As the first Devcon held in 3 years, and right after the historic Merge, Devcon VI drew thousands of attendees from around the world and our team was fortunate to be a part of it.\n" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So what were the hot topics at this year’s Devcon VI in Bogota?" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "\n" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": "1. Maximum Extractable Value (MEV)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most pervasive topics at Devcon was MEV. Flashbots co-founder Phil Daian announced the launch of SUAVE, Single Unifying Auction for Value Expression, to combat MEV centralization and censorship.\n\nSUAVE will be an MEV-aware encrypted mempool that maximizes profits for users. " + } + ] + }, + { + "item": "55844240", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "2. Office of Foreign Assets Control (OFAC)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Post Merge, OFAC compliant MEV boost relays have been enabled across a broader array of block proposers resulting in a censorship of 51% of Ethereum blocks. The implication is that if block producers can censor tx’s due to OFAC compliance, what other types of transactions could they censor?" + } + ] + }, + { + "item": "55844241", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "3. Regulation: Digital Commodity Consumer Protection Act (DCCPA) " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Regulation isn’t keeping up with the speed of innovation. This is obvious for instance in Europe’s current attempt to to regulate dollar-pegged stablecoins, capping volumes and potentially banning algo-stablecoin use. Stablecoin volume has increased dramatically over the last several years and a cap or ban could significantly hurt the crypto ecosystem overall.\n\nThe Digital Commodity Consumer Protection Act (DCCPA) could have a significant impact on DeFi in the US as well. The general consensus is that the crypto community as a whole has to advocate for positive outcomes, and resource groups like Coin Center that are actively fighting for regulatory guidance. " + } + ] + }, + { + "item": "55844702", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "5. Layer 2" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unsurprisingly, the increasingly crowded and competitive L2 landscape was on display.–The Arbitrum and Optimism booths  angled for attention side by side at the conference, an example of the escalating war for dominance between the major Layer 2 solutions. There was discussion around the security tradeoffs between side chains like Polygon and rollups like Arbitrum & Optimism, along with concerns that rollups are currently too centralized and lacking fraud proofs. " + }, + { + "url": "https://www.laconic.com/blog/what-is-a-proof", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Laconic knows a thing or two about proofs" + } + ] + }, + { + "type": "span", + "value": "…\n" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "6. Data Scaling" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Data indexing and scaling continues to be a fundamental challenge for Web3 development. You’re either running a full node yourself or forced to make compromises while using one of a handful of existing centralized indexing solutions.\nEnter Laconic, the first multichain verifiable data indexer. Laconic enables developers to build internet-scale Web3 applications and light clients at a fraction of the speed and the cost of current tools." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "*Side note, the conference wifi pw was “runfafullnode” but we’re here to do that for you…IYKYK.\n" + } + ] + }, + { + "item": "55846103", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Interestingly, the decision to host the conference in South America was prescient as the economic crises in Venezuela and Argentina highlight the use cases of crypto as a hedge against inflation and more generally as a means for financial sovereignty. We spoke with a number of Argentinians and Venezuelans about the growing usage of crypto payments in their countries.\n\nThe need to enable developers to build applications with access to faster and more reliable data has never been greater. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "item": "55844242", + "type": "block" + } + ] + } + } + }, + "featured": false, + "id": "55844243", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/laconic-governance-model.json b/json/site_content/blogPost/laconic-governance-model.json new file mode 100644 index 0000000..5a7fa9e --- /dev/null +++ b/json/site_content/blogPost/laconic-governance-model.json @@ -0,0 +1,764 @@ +{ + "data": { + "blogPost": { + "slug": "laconic-governance-model", + "title": "A New Governance Model for the New Web", + "date": "2022-10-25", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "/images/site_content/blogPost/laconic-governance-model.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network was founded to develop crucial infrastructure for current and future generations of blockchains and Web3 applications. That's an ambitious mission, and one that demands significant technological innovation. Perhaps even more innovative, however, is the company's governance model—a unique organizational " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "structure that, along with the Laconic token, underlies" + }, + { + "type": "span", + "value": " a scalable and sustainable project with the power to address the many operational, regulatory, and ideological challenges of Web3. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Operational challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network’s governance model addresses four primary operational challenges:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Network funding. The Laconic governance model funds initial network development and growth. But unlike most venture capital-funded businesses, Laconic avoids passive investment, instead requiring all founding members to make significant contributions of capital, engineering expertise, or both." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Network growth. To ensure timely continued growth, the Laconic Network must scale along with demand for Web3 data services—necessitating clear procedures for adding and removing Members and Service Providers." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Hardware operations. Laconic indexing and services are computationally expensive, requiring significant hardware and infrastructure investment. Laconic is creating a decentralized infrastructure network built for scalability and flexibility, and designed to deliver data to consumers at the highest possible service level." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Corporate governance. Clear and fair governance is essential to any decentralized project. The Laconic limited liability company agreement (LLCA) clearly specifies how stakeholders including Members, Service Providers, and data customers may participate in the governance process; proposal submission and voting processes; and proposal requirements for specific changes and updates." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Regulatory challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The governance model addresses three primary regulatory challenges: " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Making it clear that the Laconic LNT token is solely a loyalty point or prepayment for services—not a security or currency" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ensuring that on-chain private auction of membership interests in a Cayman Island LLC is fully compliant with US securities law" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Protecting minority rights for all shareholders of Laconic LLC" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Ideological challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As Web3 scales, Laconic Network aims to become an essential layer in service of verified blockchain data to applications. The company's governance model must be designed to:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Reward active Network participants through incentives for actions that help build and run the network, including becoming Members or Service Providers, writing Watchers, and consuming network data" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Discourage attempts to speculate on, or otherwise passively profit from, the network" + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Corporate structure" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic LLC structure lays out a flexible but binding legal framework for the company's relationship with Network Members. It also provides for treasury management and related off-chain governance solutions while the network is being built:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network, as a corporate entity, is a Cayman Islands LLC made up of members who are themselves corporations. Members work together to build and operate the Network, for which they are required to act as Service Providers and Validators; as a Validator, each member controls a share of the Laconic Network Liquidity Pool. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The corporation's purpose is to sell data in a way that provides purchasers with cryptographic evidence, which ensures reimbursement if a Service Provider fails to meet a quality of service guarantee. Each membership interest in Laconic LLC is indivisible and nontransferable, and represents an actual security per the S.E.C. definition of the term." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On-chain membership interests are determined based on a combination of each Member's capital accounts and individual Liquidity Pool shares—a structure designed to reward members for acting as Validators and Service Providers while promoting healthy competition. The Members’ treasury collateralizes the Network, protecting Service Providers and users." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic Network currently has seven Founding Members, who brought the project into existence by funding its treasury and providing the engineering work to create the Laconic Stack and Laconic App. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Founding Members have been selected to ensure enough initial Validators and Service Providers to run the hardware needed to index and serve data to end users." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Requiring that Members both fund the Network and provide technical services ensures that all Members are motivated to make the project successful. There is no passive investment—for example, from VCs who buy the token at a discounted price to fund the Network, then wait for a pump to sell. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Selecting seven Founding Members supports accountability for a minimum viable level of decentralization. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members operate within multiple legal jurisdictions, preventing the Laconic Stack, Validators, and Service Providers from concentrating in too few data centers or cloud providers." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As demand for the services provided by Laconic Network increases, it will be necessary to add Members in order to increase the capacity and diversity of available Service Providers. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As Members are added, Membership totals must always take the form of 6n+1—ensuring that simple majority votes will never be evenly split, and that Validators will never be paid for Byzantine fault tolerance (BFT) that they do not provide. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "New Members are added via auction. To participate in an auction, potential Members must prove that they are technically qualified and have the funds needed to buy a Membership Interest from one or more existing members." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To demonstrate their ability to operate the Laconic Stack at a level of quality comparable to that of existing Members, potential Members must participate in a testnet." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Potential Members who successfully complete the testnet are invited to a private auction of a lot of six new Membership Interests. Auction proceeds are added to the Liquidity Pool, minus a Seller’s Reward for existing Members. Seller’s Rewards, akin to mini liquidity events, provide additional financial motivation for membership. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "At some point in the future, Laconic LLC may choose to change the rules regarding membership and Validator participation." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As required by law, provisions exist by which Members may be voluntarily or involuntarily removed from the Network in order to maintain minority shareholder rights of the membership as a whole." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members wishing to exit may sell their interest back to Laconic LLC, which will then auction it to a new potential Member. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Members failing to fulfill Validator or Service Provider duties may be evicted by a governance resolution. " + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Laconic Network Token" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Much of the mechanism design of the Laconic Network is encapsulated in the intended uses of the Laconic Network Token (LNT)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": " LNT provides insurance to the Network's Service Providers and data consumers. Any prepayment for services, or any surplus services that data consumers have paid for but not used, may be redeemed for an asset of value. For Service Providers, LNT simplifies business operations, serving as a single asset representing claims against both a stablecoin and the native currency of the L1 on which they are required to operate. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The LNT also provides guarantees to Network participants, in much the same way that initiating a retail transaction triggers an automatic temporary hold on the buyer's payment card, which is released when the purchase is completed. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Note that  LNT’s purpose is to allow for refundable prepayment of services among a federation of Service Providers operating across multiple jurisdictions; the token does not exist in any other capacity. It is not a security, as it does not represent a financial interest in Laconic LLC; it can be redeemed only via authorized transfer within the Liquidity Pool, which is controlled by the Laconic Members. Membership Interests are, however, securities, as they represent both the financial interests and the governance stakes of the Members in Laconic LLC; one Membership represents one vote." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The LNT is considered a voucher for data retrieval, not a general-purpose currency. LNT is procured through the Liquidity Pool by trading 3CRV or ETH, and used to pay Service Providers for data transfers. LNT is also staked by Service Providers, and enables them to register on-chain as such. A reclamation function ensures that the full fixed supply of the token will remain constant and in use at all times, making it unsuitable for long-term holding or speculative investment. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Corporate governance for Laconic LLC will be performed on chain, by voting parties who are both Members and Validators. Voting parties may vote on two types of resolutions:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Ordinary resolutions" + }, + { + "type": "span", + "value": " govern operational details of the network and require a ⅔ vote to pass." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Special resolutions" + }, + { + "type": "span", + "value": " are required to amend the governance articles or shut down the Network. Special resolutions must be passed by a unanimous vote." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Proposals may be submitted by any user of the Network who has more than the equivalent of $1000 USD escrowed as LNT." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "New possibilities for organizational governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s primary goal is to make verifiable blockchain data available at scale to Web3 applications. The company also aims to embody the ideological goals of true decentralization and fair rewards for parties actively participating in the network as Members, Validators, Service Providers, or Watcher writers. The Founding Members have also thought extensively about the utility of the LNT token (as a voucher for data), its role in governance (none at all), and how to grow the network through Validator/Member auctions. Together, these choices yield a novel corporate governance model worthy of study by any organization with similar goals and challenges." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "55641249", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/laconic-watchers.json b/json/site_content/blogPost/laconic-watchers.json new file mode 100644 index 0000000..beaaaf5 --- /dev/null +++ b/json/site_content/blogPost/laconic-watchers.json @@ -0,0 +1,695 @@ +{ + "data": { + "blogPost": { + "slug": "laconic-watchers", + "title": "Laconic Watchers: Ensuring Trustlessness in Web3", + "date": "2022-09-06", + "category": [ + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/laconic-watchers.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Watchers in the Laconic Network are the keystone technology that will scale blockchain data access for the next wave of Web3 adoption. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic stack provides a way for DApp developers to retrieve the blockchain data they require in a manner that is efficient, verifiable, and decentralized. The mechanism by which the relevant subset of blockchain data is queried, cached, and delivered, is the Laconic Watcher." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve previously explored " + }, + { + "url": "https://www.laconic.com/blog/how-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "how the Laconic Network uses IPLD" + } + ] + }, + { + "type": "span", + "value": " (“the data model of the content-addressable web”) to keep off-chain data cryptographically verifiable across transformations. The Laconic Watcher is the component that makes that data available for DApps.  You will learn about the role of Watchers within the Laconic Network, how they work, and what you can use a Watcher for." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "DApp Developer Challenges" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A public, permissionless blockchain is optimized for storing large amounts of data and achieving consensus. A DApp typically needs only a tiny fraction of that data for its operations. Ethereum clients are not designed to serve the data needs of a given DApp, such as reading and linking data from multiple contracts, introducing cross-chain data, or presenting DApp-specific information to users. Existing options for retrieving blockchain data have some drawbacks:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Significant effort required: " + }, + { + "type": "span", + "value": "Running an archive node gives access to all of a blockchain’s data.  Structurally, blockchains are write rather than read optimized. Many developers are unable to, unwilling to, or cost-prohibited from, running the infrastructure." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Trustlessness broken: " + }, + { + "type": "span", + "value": "Centralized services can provide indexed blockchain data, but  they lack the mechanisms to cryptographically prove their accuracy and provenance. DApp developers and users must trust these services, contrary to the decentralized and trustless promises of Web3." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our goal is to make DApp development as frictionless as possible. When you build your DApps, Laconic Watchers relieve you of the infrastructure burden and enable you to query and receive trustless, verifiable, off-chain blockchain data." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "The Watcher and the Laconic Network" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To understand the Watcher’s role and functionality, let's briefly recap how the Laconic solution works." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The primary function of the Laconic Network indexing is to keep an up-to-date version of blockchain data in a relational database. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To achieve this, a Laconic Full Index Node fetches the latest blockchain updates and turns them into IPLD blocks with advanced indexing. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike existing ETL (Extract-Transform-Load) solutions that convert blockchain data into a searchable database format, the Laconic Network continuously monitors and evaluates state diffs on the chain, retrieving and indexing relevant tries." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This additional data opens up a new range of opportunities for DApps. This is the first component of the Laconic solution.  However, the database is still too large and too general for DApps to use; raw blockchain data structures are not suitable for DApps." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Enter the Laconic Watcher." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Your DApp’s Personal Data Delivery Service" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Watchers are the component that makes DApp development as frictionless as possible. Watchers serve three fundamental purposes:" + } + ] + }, + { + "type": "list", + "style": "numbered", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Query" + }, + { + "type": "span", + "value": " the Laconic Full Index Node for the specific data your DApp needs." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Transform" + }, + { + "type": "span", + "value": " the queried data to make it consumable for your DApp." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cache" + }, + { + "type": "span", + "value": " the data for fast and inexpensive access." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Watchers run queries relevant to your DApp through a GraphQL interface; you can create precisely the API you need, transforming the data as required. Watchers run as daemons, constantly updating the cache with transactions from the blockchain's head." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your Watcher is effectively your personal, virtual, read-only blockchain, containing the specific data your DApp needs. You get the same verifiability you would from the source blockchain, without the same burden or overhead." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Where do I find Watchers? How can I write one? Do I have to?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Running Watchers: " + }, + { + "type": "span", + "value": "It is the job of Service Providers on the Laconic Network to run Watchers. One or more Service Providers can run a given Watcher." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Do I have to write my own Watcher?" + }, + { + "type": "span", + "value": " As a DApp developer, you don’t necessarily need to create your own Watcher. Some alternatives:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Pay to access a public Watcher API. " + }, + { + "type": "span", + "value": "The fees are split between the Service Provider and the Watcher creator." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Check the Watcher Registry:" + }, + { + "type": "span", + "value": " If a Watcher’s creator chooses to make it publically available, they can add the Watcher to the on-chain Watcher Registry for easy discovery. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "You don’t have to start from scratch. " + }, + { + "type": "span", + "value": "Watchers are composable. You can build a Watcher on top of one or more existing Watchers found in the Watcher Registry." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What about Smart Contracts? " + }, + { + "type": "span", + "value": "As a Smart Contract author, you can auto-generate Watcher code directly from your Smart Contract's Solidity source code. The code generator is capable of generating the Watcher GraphQL API based on eth_calls as well as storage variables in the Smart Contract." + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Watchers, in Conclusion" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Watchers" + }, + { + "type": "span", + "value": " allow DApps to cache and query verifiable blockchain data served by a decentralized network of Service Providers. Watchers facilitate the creation of custom APIs suitable for a DApp’s specific needs. " + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "As a developer," + }, + { + "type": "span", + "value": " you will benefit from the Laconic Network by finding, writing, or combining Watchers to serve the backend data needs of your DApps." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "As a Laconic Network Service Provider or Validator," + }, + { + "type": "span", + "value": " you will be part of solving the problems of blockchain data access for the next wave of Web3 adoption. " + } + ] + } + ] + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Stay up-to-date with Laconic News" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Join our " + }, + { + "url": "https://discord.com/invite/ukhbBemyxY", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Discord server" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Subscribe to our " + }, + { + "url": "https://t.me/share/url?url=https%3A%2F%2Fwww.laconic.com%2Fblog%2Fhow-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Telegram channel" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Sign up for our mailing list below" + } + ] + } + ] + } + } + }, + "featured": true, + "id": "55448692", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/monthly-update-dec-2022.json b/json/site_content/blogPost/monthly-update-dec-2022.json new file mode 100644 index 0000000..1a5e1f9 --- /dev/null +++ b/json/site_content/blogPost/monthly-update-dec-2022.json @@ -0,0 +1,972 @@ +{ + "data": { + "blogPost": { + "slug": "monthly-update-dec-2022", + "title": "Laconic Monthly Update: December 2022", + "date": "2022-12-01", + "category": [ + { + "id": "2965426", + "slug": "news", + "title": "News" + } + ], + "author": { + "id": "63259964", + "name": "Maly Ly" + }, + "image": { + "url": "/images/site_content/blogPost/monthly-update-dec-2022.png" + }, + "content": { + "blocks": [ + { + "id": "63259974", + "title": "metamask" + }, + { + "id": "63259975", + "title": "testnet" + }, + { + "id": "63259976", + "title": "lp" + }, + { + "id": "63259977", + "title": "public marketing launch" + }, + { + "id": "63260091", + "title": "nft" + }, + { + "id": "63259978", + "title": "events" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Hello Laconians," + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Welcome to our first Laconic Monthly Update! " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth development, we were excited to go live with our public marketing launch in the last quarter." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In the three months since, Laconic has grown quickly, and we’ve made huge progress with both product and growth. Despite the arrival of “crypto winter,” our team remains focused on building a platform with the power to ensure that the next generation of blockchain applications are safer, faster, and more useful." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thank you for joining us on our journey!" + } + ] + }, + { + "item": "63259974", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "METAMASK PARTNERSHIP" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This month, we announced that Laconic is partnering with the world’s leading self-custodial wallet, ConsenSys’s MetaMask, and MetaMask founder Dan Finlay, to launch MobyMask, an anti-phishing tool. Laconic's MobyMask-caching Ethereum light client greatly reduces the cost of hosting a trustworthy copy of the MobyMask anti-phishing registry, making the tool accessible and affordable for a wide range of users." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Together, we’re laying the groundwork for an ambitious shared vision: a system with the ability to verify credible sources for a wide range of information types. You can " + }, + { + "url": "https://www.laconic.com/blog/laconic-and-consensys-metamask-launch-mobymask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "read our blog to learn more about the partnership with MetaMask" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259975", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "TESTNET" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve successfully launched a testnet for internal development and for testing with Laconic’s Founding Members (Founding Members to be announced). The genesis transaction occurred on August 9, 2022, with all Members in consensus.\n\nWe’re now creating tooling to monitor block production, number of transactions, and Validator health; further tooling will include a dashboard and a block explorer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A public, incentivized testnet is planned for 2023. You can sign up for early access " + }, + { + "url": "https://www.laconic.com/partners#testnet", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "here" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259976", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "LIQUIDITY POOL & PAYMENT CHANNELS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve continued work on the mechanics and logistics of the liquidity pool, state/payment channels that will govern the flow of payments and tokens between our own chain and Ethereum, and auction logic implementation. We’ll also be building out the back end of the Laconic App and its network underpinnings. The next step is to deploy the smart contract for the liquidity pool directly to testnet. " + }, + { + "url": "https://www.laconic.com/blog/introducing-laconic-network", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Learn more about our platform and network" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "item": "63259977", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "PUBLIC MARKETING LAUNCH" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "After five years in stealth, we completed the design and development of the Laconic website and community channels, with a successful public launch on August 16. Since then, we’ve scaled online engagement with potential partners, developers, press, and people around the world looking to learn more about Laconic. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "item": "63260091", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "WEEKLY GROWTH CAMPAIGNS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve launched 14 campaigns since the website launch, and will continue to scale marketing and community efforts in 2023." + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "July 26:" + }, + { + "url": "https://www.laconic.com/blog/introducing-laconic-network", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Introducing Laconic Network" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "July 26:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-different", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Network is Different" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "August 6:" + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watchers: Ensuring Trustlessness in Web3" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "August 30:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-network-uses-ipld", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Network Uses IPLD" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 6:" + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic Watchers: Ensuring Trustlessness in Web3" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 13:" + }, + { + "url": "https://www.laconic.com/blog/rick-dudley-on-interchain-fm", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Rick Dudley Discusses Laconic Network on Interchain.fm" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 20:" + }, + { + "url": "https://www.laconic.com/blog/what-is-a-proof", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "What Is a Proof and Why Do You Need One?" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "September 28:" + }, + { + "url": "https://www.laconic.com/blog/99-problems-but-nfts-aint-one", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "99 Problems But NFTs Ain’t One" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 5:" + }, + { + "url": "https://www.laconic.com/blog/how-laconic-improves-the-nft-experience", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "How Laconic Radically Improves the NFT Experience" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 20: " + }, + { + "url": "https://www.laconic.com/blog/laconic-devcon-vi-recap", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic’s Devcon VI Recap" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "October 25: " + }, + { + "url": "https://www.laconic.com/blog/laconic-governance-model", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "A New Governance Model for the New Web" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "November 8: " + }, + { + "url": "https://www.laconic.com/blog/laconic-and-consensys-metamask-launch-mobymask-light-client", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Laconic and ConsenSys’s MetaMask Launch MobyMask" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "November 22: " + }, + { + "url": "https://www.laconic.com/blog/why-decentralization-matters", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Why Decentralization Matters" + } + ] + } + ] + } + ] + } + ] + }, + { + "item": "63259978", + "type": "block" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "EVENTS" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Nothing beats real-life engagement! Conferences and events are key channels for growing audience awareness. Our team canvassed select Ethereum and blockchain conferences with a focus on brand positioning, partnerships, and customer development:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ETHAmsterdam and Devconnect (4/18–25): " + }, + { + "type": "span", + "value": "Going into the launch of our public marketing channels, our team produced " + }, + { + "url": "https://twitter.com/laconicnetwork/status/1521957074841706497", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "a networking event" + } + ] + }, + { + "type": "span", + "value": " to connect with Member teams, investors, and developers attending ETHAmsterdam and Devconnect, a conference dedicated to Ethereum L2 Scaling." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "ETH CC Paris (7/18–22): " + }, + { + "type": "span", + "value": "Multiple Laconic Member teams converged at the largest Ethereum conference. This was a great opportunity for us to deepen awareness of Laconic among investors and validators in the Ethereum community. " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cosmoverse Medellin (9/27–28): " + }, + { + "type": "span", + "value": "Cosmos’s largest conference was an opportunity for us to build awareness and engagement in the Cosmos ecosystem." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Devcon Bogota (10/7–16): " + }, + { + "type": "span", + "value": "The largest Ethereum developer conference offered the perfect opportunity to drive awareness of Laconic with Ethereum developers. Check out the " + }, + { + "url": "https://www.laconic.com/blog/laconic-devcon-vi-recap", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "recap blog" + } + ] + }, + { + "type": "span", + "value": " to learn what the hot topics were at Devcon." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "SF Blockchain Week and ETH SF (11/1–5): " + }, + { + "type": "span", + "value": "These two Bay Area conferences enabled our team to deepen our network in one of the world’s premier tech hubs." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\n\n" + } + ] + } + ] + } + } + }, + "featured": true, + "id": "63259979", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/rick-dudley-on-interchain-fm.json b/json/site_content/blogPost/rick-dudley-on-interchain-fm.json new file mode 100644 index 0000000..9396ab2 --- /dev/null +++ b/json/site_content/blogPost/rick-dudley-on-interchain-fm.json @@ -0,0 +1,600 @@ +{ + "data": { + "blogPost": { + "slug": "rick-dudley-on-interchain-fm", + "title": "Rick Dudley Discusses Laconic Network on Interchain.fm", + "date": "2022-09-13", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "/images/site_content/blogPost/rick-dudley-on-interchain-fm.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick Dudley, the co-founder of " + }, + { + "url": "https://laconic.com", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "the Laconic Network" + } + ] + }, + { + "type": "span", + "value": ", talks to Chjango Unchained on a special live-streamed episode of " + }, + { + "url": "https://www.youtube.com/watch?v=viE0BZGx_DU", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Interchain.FM" + } + ] + }, + { + "type": "span", + "value": " about the risks of monopolization, why DApps have a hard time querying Ethereum data, how the Laconic Network solves this problem, and the legal aspects of creating a truly decentralized DApp data marketplace." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "How Laconic preserves verifiability" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic is a data indexing service with an Ethereum L2 rollup that uses the Cosmos SDK and a fork of Ethermint. “Primarily, what we're doing is making third-party verifiable caches and indexes of Ethereum data, and we're building a marketplace to facilitate the buying and selling of that data.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For Ethereum-based apps, developers only need a subset of the Ethereum state to run their DApp. Laconic connects you with service providers who can serve that data to DApps, all in a decentralized, disintermediated way." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Unlike other DApp data providers, Laconic focuses on the verifiability of the provided data. Rick points out that “there is no other platform that is preserving the verifiability in the way that we are.” Achieving this verifiability is anything but easy. If a DApp wants to run verifications using an archive node, it would take months of moderate computing time to do that, not to mention archival storage. “Most people don't have 14 terabytes just laying around that they can attach to their phone or laptop.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s answer is to split the data and decentralize validation. As Rick explains, “we take the whole Ethereum blockchain. And then we get a bunch of validators together and make you a little mini proof-of-stake blockchain of just the information you care about.” Ethereum remains the L1 in this scenario; “all the value that's transacted on the Laconic Network is actually staked or escrowed on Ethereum.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Leveraging linked data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic solution builds upon an established data format (IPLD, or Interplanetary Linked Data) to deliver the promise of verifiable data. The IPLD format not only allows transforming data to better serve the needs of DApps, but also makes it possible to verify these transformations. “It's not magic, but it makes it easier for us to deal with these very complex and robust data types. It also allows us to do cross-chain proofs, which is the main appeal.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Not only cross-chain proofs, but proofs about Ethereum events—which is not possible in Ethereum directly. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "“Events were never intended to be provable there. They weren't supposed to be used in the way they're used today.”  Rick explains how difficult it is for the average DApp developer to know that a single event really came from Ethereum, “You have to have the hundreds or thousands of events that came with that event, or you have to rerun the transaction in the block and see that it emitted the event. And both of those things are extremely expensive.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic has a technique for saving the effort of doing that re-computation, and IPLD plays a large part in that." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Acting lawfully" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network will have up to 67 validators that will be located in different countries. This raises some legal questions. Chjango points to a scenario where sanctions against countries can break the promise of decentralization. Rick explains how Laconic’s legal structure addresses this issue. Laconic is run by an LLC that is designed to “comply with the laws within its jurisdiction and to allow each member to comply with the laws within their jurisdiction.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If a country imposes sanctions on another country, the members are free to follow their local law and refuse service to members of the sanctioned countries. At the same time, the rest of the Laconic Network remains available for those accounts. All this can be done without sacrificing privacy. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "“You can use encryption and other things for someone to say ‘is this user in the United States, yes or no,’ without revealing where the user actually is.” Rick concludes that, “of the 67 members, we hope to have members that are willing to and legally able to service customers in those jurisdictions that are currently underserved.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Blockchain decentralization, or the lack thereof" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization in conjunction with global distribution is key to providing uninterrupted service in the face of ill-intended governments or political disturbance. Rick considers decentralization to be retreating, giving way to new monopolies. In other words, there is a re-centralizing effect." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The problem starts with the cost of verification. Chjango asserts that “the difficulty of fully verifying an Ethereum blockchain is such that it has hurt its decentralization, even though the underlying blockchain is decentralized.” The expense of running a fully validating node has introduced new centralization choke points." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick places the problem one level down, at the network. The internet comprises 60,000 sub-networks that communicate over the Border Gate Protocol (BCP). But a traffic analysis found Ethereum network traffic on only 18 subnets. And a handful of companies – AWS, Google, Microsoft, Cloudflare –  run much of the infrastructure, and are also in the same legal jurisdiction. So a single company can switch off large parts of the Ethereum system." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This lack of decentralization continues at the blockchain level. “There are 4000 validating nodes on the network, but about 80% of them are in AWS. And somehow, in spite of that, 50% of the blocks still come from three people.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic's answer to this problem is “forced decentralization.” Rick stated, ”Laconic requires its members to run their own hardware and to be located in different data centers. And we may at some point require people to run their own networks on the internet, so that we do maintain decentralization at that level.” Here, the legal side plays an important role.  The LLC can positively assert that a Member is a specific legally registered entity. They signed a contract that stipulates they avoid being in the same data center as another Member, or else they're in breach of the contract and they risk being removed from the network. \"That's a big benefit of the LLC, and definitely something that we've spent time thinking about to design it right,\" Rick continued." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Monopolistic forces are unavoidable. Give people an option to exit a toxic monopoly" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Even with Laconic’s solution, scalability and sustainability of the whole blockchain ecosystem are not a given. As pointed out earlier, more than 50% of Ethereum blocks are created by only three mining pools." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "(Source: " + }, + { + "url": "https://etherscan.io/stat/miner?blocktype=blocks", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Etherscan.io" + } + ] + }, + { + "type": "span", + "value": ")" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Such monopolization tendencies are hard to fight. Rick’s view is to initially accept the inevitable. “I think it's more about acknowledging that these things are going to be monopolized. And then building technology that allows us to exit those monopolies as needed, more than trying to build technology that's going to resist the monopoly.”" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The aim of Laconic is to make it trivial for people to exit when those systems get monopolized. To achieve this, Laconic has a few layers of protection in place to stop the Laconic Network from being compromised by monopolization attempts. As an example, the legal agreements prevent Laconic members from sharing investors. So an investor cannot simply invest in over one-third of the members to take it over." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But if some sort of monopolization does occur, every DApp user has an option to exit. “If every user of a DApp has their own Watcher (Laconic’s API servers) running, they would have all the state they needed to move that DApp to another chain. They could take that one Watcher, generate a new state snapshot for that application, and then move that state snapshot to a new chain. And that literally would take a year of processing on Ethereum." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What’s in the name Laconic?" + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "Why “Laconic”? " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Rick explains, “It means terse.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Referencing the ancient inhabitants of Laconia known for their more concise and terse style of speech, Laconic also describes a style of speaking or writing that uses only a few words, often to express complex thoughts and ideas." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic’s “laconic” technology  elegantly simplifies the complexity of blockchain, and accelerates DApp development." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Takeaways" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This was a wide-ranging interview with deep-dives on many topics not covered here. Dive in to find out more about:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Payment channels and validator accountability." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Processing time and why they don’t discuss network speed or TPS at Laconic." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Why Laconic chose Geth over other Ethereum clients." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Catch the full interview on " + }, + { + "url": "https://www.youtube.com/watch?v=viE0BZGx_DU", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "YouTube" + } + ] + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Thanks to " + }, + { + "url": "https://www.youtube.com/channel/UCMcYl850ni93ZDqchYF7v1w", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Chango Unchained" + } + ] + }, + { + "type": "span", + "value": " and Interchain.FM for leading this thought-provoking interview with Rick. It’s exciting to be able to showcase the work happening at Laconic, designing and implementing a solution for truly decentralized delivery of provable data from the Ethereum blockchain." + } + ] + } + ] + } + } + }, + "featured": false, + "id": "55457818", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/rick-dudley-on-the-interop.json b/json/site_content/blogPost/rick-dudley-on-the-interop.json new file mode 100644 index 0000000..9bb5766 --- /dev/null +++ b/json/site_content/blogPost/rick-dudley-on-the-interop.json @@ -0,0 +1,461 @@ +{ + "data": { + "blogPost": { + "slug": "rick-dudley-on-the-interop", + "title": "Rick Dudley Discusses Laconic Network on The Interop", + "date": "2023-02-09", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "/images/site_content/blogPost/rick-dudley-on-the-interop.png" + }, + "content": { + "blocks": [], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic cofounder Rick Dudley appeared on a special livestream of The Interop with host Sebastien Couture to discuss the Laconic Stack, the blockchain data problems that Laconic solves, Laconic’s novel governance structure, and how Laconic can index and verify data faster, more efficiently, and at lower cost. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Below is a distilled transcript of Rick’s responses during the discussion." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The Future is App Chains" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think there will be millions of chains, and we'll be using a combination of rollups and mesh–not straight linear L1, L2, L3, but also meshes of rollups and attestations publishing bridges, etc. And although we may have millions of chains, we won't have millions of massive chains. A large chain may have 100 members, and there may be one or two chains out there with 4,000 validators. But in the world, you only need a few of those." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "I think everything becomes an app chain. I think mainnet Ethereum ultimately becomes an app chain and the application is settling rollups–very similar to Cosmos Hub, frankly. Polkadot, Ethereum 2.0, Cosmos Hub are all actually very similar in terms of the endgame state in the final thesis. And I don't think that there will necessarily be a winner per se. I think they will have curious different properties. " + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Why Laconic?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The ultimate goal of Laconic is to get all of the data that a user is concerned about in the hands of that user. Not in a cloud-hosted environment, not in Microsoft, not in AWS, but in users’ actual custody. And to enable them to do all the verification themselves." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Right now, it’s very difficult to extract parts of data from the Ethereum Mainnet that are relevant for Dapp needs. It’s almost impossible to synchronize a Geth node in a reasonable amount of time." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are multiple light client protocols that have come around to help alleviate this problem but they still don't go all the way. The Laconic Network goes the whole way. It goes from source code, to what is in the user's eyeballs with everything being verifiable. If you see a message on Laconic that came to you through the Laconic Network, you could say, \"I want to know which blockchain or blockchains this came from. I want to know what code generated this result. I want to know who wrote that code.” We provide all of that in the Laconic Network." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Three Major Components of Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is the Laconic LLC itself, which is in the Cayman Islands. There is the Laconic Stack, which is the standalone software that anyone can run today to generate this data and the evidence that they need. And then there's the Laconic Network, which facilitates the buying and selling of data. It facilitates running these services, discovering the services, paying for services, and then making sure all of that is verifiable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Those three components are an evolution. We've iterated on the Stack many times over at this point. MakerDAO is still using an early version of that stack to this day last I checked, which was recently. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you were an intrepid developer, you could go into the Stack Orchestrator code and run that yourself and put that into production yourself right now. But the problem with that is it's very expensive to generate this evidence. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s computationally very expensive, and specifically, disk I/O operations are very expensive activities to do. So as a Dapp developer, when you have very few users, you can run this reasonably on the laptop. But as your app grows, or if you're wanting to see all of the Uniswap V3 pool data, then a laptop's not going to be able to process that in a timely manner necessarily. I mean, laptops are pretty powerful so some of them can, but maybe not all of them. And at that point, you need hardware. And when you need hardware, you then have this problem of, \"Okay, well am I going to buy hardware and rack it in a data center?\" That's probably not a viable answer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Am I going to go to AWS? Well, AWS is centralized, there are all sorts of problems. There's censorship for instance. AWS may choose to comply with a law that I'm not legally obligated to comply with. We've seen this issue with Alchemy and Infura, and these solutions comply with the laws in their jurisdiction, but the Dapp developer is in a different jurisdiction. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So then you end up with this situation; \"Okay, well if I want to have multiple service providers actually serving this data to users, they need to be in multiple jurisdictions.\" And that's what Laconic LLC solves. It's a Cayman Island LLC. We have members in different jurisdictions and those members will contract with the end users and comply with those laws in that way." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and Cosmos" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic team members were also core contributors to Cosmos SDK–we did a bunch of work on the Cosmos SDK. The data structures in Ethereum and the data structures in Cosmos and many other blockchains were designed to facilitate consensus, not to facilitate reading the data back out. And so in those architectures, there's utility in taking the techniques that we've applied to Ethereum and applying them to those other chains." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There is a value and utility to taking those techniques and applying them to the Cosmos SDK chains. Osmosis is an example of where it would be useful. For example, you can't have a block explorer that works across Cosmos Hub upgrades. No one's ever bothered to build one that works that way. If you built the block explorer on top of Laconic instead of directly on top of the chain, you would actually be able to provide that continuity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Every time a Cosmos chain upgrades, they regenesis and restart the chain. When you start that new genesis, people–just as a matter of convenience–don't preserve that data. You don't have a way of representing the irregular state change that happens during the upgrade. Whereas in the Laconic system, we have a means of doing all those things. We can link any two arbitrary chains together and we have a means of representing these arbitrary state changes. We can provide that continuity as a service." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Incentive Alignment " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Because we're IPLD based, we actually can relatively easily take our archive and push it into Filecoin, where there can then be this clear monetization strategy for storing the data. Because we monetize the transmission of the data, which is a much easier problem to solve than the verifiable storage of Filecoin, we're providing an incentive for why someone would do that. Think about it. There are different incentives throughout the process. There's an incentive for including the transaction. That incentive is very clear, but there's not really any incentive in any blockchain I'm aware of for why I should then send that data. Why should I satisfy a read from a user? A user asks for a read, and why do I care?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That's what Laconic is trying to solve–we're incentivizing the reading of that data. And by incentivizing the reading of that data, that's step number two. Now we can talk about the incentives of step number three, which is a long-term persistent storage of that data. Because if you think about just having the incentives of just Filecoin and just Ethereum, you have this gap in the middle. Why do I take the Ethereum data and transform that and publish it to Filecoin? There's not really an incentive for me to do that." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Whereas with Laconic, there starts to become more of an incentive to do that because I need to support my own read infrastructure. People will come to you and know to come to a single place to get their historic reads as well as their more recent reads. And so you’ll be incentivized to charge them. There will already be an ecosystem in place where people are accustomed to paying for data. And when they want to pay for old data or new data, they'll come to the same place, buy that data, and that will incentivize archival storage. Right now we don't have a very good model for why archival storage persists. And it is a real mechanism design issue actually." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic and IPLD" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "InterPlanetary Linked Data (IPLD) is the core of our system. The first thing we do is take the Ethereum data, which could be any blockchain or any hash linked data structure, and we convert that into an IPLD object. We then index it in that context. We’re storing the RLP encoded bytes, but we are also storing the CID (Content ID), the multi-format address of that object. That's how we're able to generate evidence." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On Ethereum, you have transaction receipts and you have the event messages. When you have an event message on Ethereum, the event message does not prove all the way back up to the root. So when you have a set of events, which is what The Graph consumes, the way that you prove that event is correct is that you find the block that that event was in, and then you rerun that whole block and at the end of it you see if you have the same event that you started with. Whereas, if I have an account balance on Ethereum, I have a block number and then I can get a proof. So I don't have to recompute the whole block to figure out the account balance in that block. I just get the proof from the Ethereum client about that account balance at that block, and I can present that proof and the balance to the user using eth_getProof." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "But the actual logs in Ethereum are not provable in this way. This is why The Graph isn't provable and there are a lot of consequences from this. But because we use IPLD, we can create those hash links. Where the link was missing in the original Ethereum protocol, we can augment that protocol and generate a proof using the Ethereum data and our additional links, which are relatively easily. It's not some weird, crazy different format. It's this format that is very similar to the existing Ethereum formats, that prove that this log actually came from this block." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Laconic Member Validators" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Laconic L2 has seven Founding Members right now. These seven Members validate, ingest the blocks, and make commitments to the state of those blocks. They then share that information with a paying customer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There are plans to increase the validator set. From a customer perspective, if our customer is a Dapp developer and they're saying, “right now I have to use Infura, Alchemy, Blocknative to assert that my data is correct because if one of them goes down for whatever reason, that's three right there.” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "That sounds like a pain in the ass. With Laconic, you integrate one protocol and you get seven Member Validators instead of three, and you get an assertion from us that you can verify yourself that we're actually physically located in different places. Alchemy and Infura both run in AWS, I presume. If AWS goes down, you just lost two out of your three, if not all three out of your three in that case. Seven is a low number, but seven is incredibly high compared to what people have right now, or they think they have four and they have one, whereas we're positively asserting that you have seven." + } + ] + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "RPC Services and Laconic" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "On the path to building Watchers, we realized we had to build extremely performant RPC endpoints, and we had to build out a deployment system. We realized that that was actually what people wanted to buy from us. Most Dapps don’t want to bother with Watchers right now. What they want to see is this immediate savings on the RPC endpoint side. From there, oncet our foot is in the door, we can say, \"Well, we can give you even more savings. You are using that RPC endpoint to build your own indexer. We have a whole library of tools to build indexers that will auto-generate indexers for you. And we have a marketplace where you can go to get other people to run that indexer for you when you don't want to scale it.\"" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Currently, RPC endpoints are subsidized by VCs. Dapp developers are never experiencing the true cost of running an indexing service or running an RPC endpoint. They're not exposed to that in a free market way. There's this actor, this venture capitalist, who is going in and giving away free samples at a massive scale. The challenge for us is in how we compete with that? There's also a challenge in that our customers are depending on this centralization service and don't realize it. " + } + ] + } + ] + } + } + }, + "featured": false, + "id": "64080923", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/trends.json b/json/site_content/blogPost/trends.json new file mode 100644 index 0000000..d643d0b --- /dev/null +++ b/json/site_content/blogPost/trends.json @@ -0,0 +1,570 @@ +{ + "data": { + "blogPost": { + "slug": "trends", + "title": "A Look Ahead at Crypto in 2023", + "date": "2023-01-05", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55457832", + "name": "Michael Gushansky" + }, + "image": { + "url": "/images/site_content/blogPost/trends.png" + }, + "content": { + "blocks": [ + { + "id": "63885761", + "title": "Prop 82" + }, + { + "id": "63885758", + "title": "Akash Validator Set" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "So much has changed in the last year due in large part to a seemingly unpredictable sequence of epic market failures. Despite this, we’re still here trying to predict the unpredictable. As the dust settles from a cascade of collapses including FTX, Terra, 3AC, and many others, the direction crypto must take to become viable is now clearer. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We find ourselves in a strange position. We built protocols that actually work under immense market pressure, showing some of the promise of decentralized systems. However, that pressure came from deeply irresponsible market manipulation and behavior. How do we make sense of the fallout, and do better. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Blockchain Regulation: it’s coming" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First, this space is about to get regulated. The " + }, + { + "url": "https://www.pillsburylaw.com/en/news-and-insights/digital-commodities-consumer-protection-act-digital-assets.html", + "type": "link", + "children": [ + { + "type": "span", + "value": "Digital Commodities Consumer Protection Act" + } + ] + }, + { + "type": "span", + "value": " (DCCPA) looms in the wake of FTX’s demise and Sam Bankman Fried’s increasingly radioactive support of the bill. The DCCPA aims to give the CFTC primary jurisdiction over crypto exchanges. This would establish the commission as the authority on spot, margined and leveraged digital commodity trades. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The bill, however, does not outline processes for classifying assets as commodities and securities. This is relevant because the Securities and Exchange Commission (SEC), which under Gary Gensler has increasingly strived for more oversight, currently qualifies most crypto, aside from Bitcoin, as a security. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In Europe the new Markets in Crypto Assets Regulation (MiCA) aims to regulate dollar-pegged stablecoins, controlling transaction count and volume limits which, " + }, + { + "url": "https://www.reuters.com/technology/eu-crypto-rules-set-cap-dollar-pegged-stablecoins-2022-10-07/#:~:text=LONDON%2C%20Oct%207%20(Reuters),competitiveness%2C%20industry%20representatives%20have%20said.", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "according to Reuters" + } + ] + }, + { + "type": "span", + "value": ", are already exceeded by the three largest stablecoins, Binance USD, USD Coin & Tether. Stablecoin volume has increased dramatically over the last several years and a cap or ban could hinder the crypto ecosystem overall. Crypto regulation and the conversations around asset classifications, regulatory purview, and stablecoin usage will be defining battlegrounds in 2023. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Decentralization: yes it matters" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Increasing regulatory pressure will force crypto projects to re-focus on achieving actual decentralization; not just to avoid legal consequences but also to empower more equitable and effective on-chain governance. Although there is no official guidance from the SEC, commissioner " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Hester Pierce’s Safe Harbor Proposa" + }, + { + "type": "span", + "value": "l has served as a guiding light for those interested. The proposal suggests networks must achieve sufficient decentralization within three years, meaning networks should not be controlled by a single individual or group of individuals. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Tokens must also be used to exchange value pertaining to the network. The token should be sold for facilitating access to, participation on, or development of the network. Essentially, if it’s a decentralized network, validators/investors/teams cannot control majority stake. If the network has a token, then the token needs to be used for something " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "actually" + }, + { + "type": "span", + "value": " intrinsic to network operations–it can’t just be a fundraising vehicle." + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "On-Chain Governance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We’ve touched on a number of external forces impacting networks but what about internal forces? On-chain governance is rapidly evolving. Usage of proof of stake (PoS) consensus has grown over the last several years in part due to a dramatic reduction in power consumption, and also because of increased flexibility to affect significant changes to network parameters. With voting power relative to stake, stake concentrated in the hands of a few entities can present unique and potentially insurmountable challenges to achieving sufficient decentralization." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Cosmos ecosystem is a particularly fascinating testing ground for on-chain governance. From the controversial " + }, + { + "url": "https://www.mintscan.io/juno/proposals/16", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Juno Prop 16" + } + ] + }, + { + "type": "span", + "value": " which resulted in on-chain confiscation of user funds, to the equally controversial " + }, + { + "url": "https://www.mintscan.io/cosmos/proposals/82", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Cosmos Prop 82" + } + ] + }, + { + "type": "span", + "value": " which put forward an entirely new paradigm for Cosmos Hub utility and ATOM value capture, the Cosmos ecosystem has been trailblazing governance processes and implementation. " + } + ] + }, + { + "item": "63885761", + "type": "block" + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "What’s at Stake?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the main issues we’re seeing now is the disproportionate concentration of stake. In Cosmos for instance, the top 5 out of 150 " + }, + { + "url": "https://www.mintscan.io/juno/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Juno validators" + } + ] + }, + { + "type": "span", + "value": " control 25% of the network. The top 5 out of 175 " + }, + { + "url": "https://www.mintscan.io/cosmos/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Cosmos validators" + } + ] + }, + { + "type": "span", + "value": " control 25% as well. The top 5 out of 100 " + }, + { + "url": "https://www.mintscan.io/akash/validators", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Akash validators" + } + ] + }, + { + "type": "span", + "value": " control 37%. " + } + ] + }, + { + "item": "63885758", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In Ethereum, " + }, + { + "url": "https://cointelegraph.com/news/64-of-staked-eth-controlled-by-five-entities-nansen", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "according to Nansen" + } + ] + }, + { + "type": "span", + "value": ", there are 5 entities that control 64% of staked Ether. Although only 11% of circulating ETH is staked, among 426,000 validators, three major cryptocurrency exchanges account for 30% of the total staked ETH, with Lido DAO accounting for 31%. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If the top 4-5 Validators control 1/4 of the stake and, at times more, of a network, is the network really decentralized? If an exchange like Binance or Huobi can buy up large volumes of tokens, enter the top validator slots, and through governance, make substantive changes to the network for their own benefit, is that network truly decentralized? There are valid arguments on both sides. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "There’s an argument that these networks are permissionless and any entity can engage in governance by purchasing tokens and staking them, so in theory, they are decentralized. In practice, power is often concentrated in the hands of a few entities with a majority of the tokens. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The current Cosmos governance landscape is also fascinating because many pivotal governance mechanisms and network changes are being deliberated simultaneously. Should there be a constitution? Should there be larger deposit requirements for proposals? Should ATOM issuance be changed to fund network growth initiatives–completely repurposing the network in the process? Cosmos will continue to innovate on-chain governance. As Cosmos gains more visibility, developers will adapt components of Cosmos governance for their purposes. " + } + ] + }, + { + "type": "heading", + "level": 1, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Novel Solutions to Crypto’s Most Existential Problems" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As we look ahead to 2023, Laconic is well positioned to address these issues. Laconic Network leverages Cosmos technology and is run by an LLC that is designed to comply with the laws within its jurisdiction and to allow each member to comply with the laws within their jurisdiction. Geographic distribution of member validators strengthens the network's resilience to nation specific sanctions. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Additionally each member validator is given one vote and all votes are equally weighted. This eliminates uneven distribution of voting power typically seen in delegated proof of stake networks. Laconic Network also makes it clear that Laconic Network Token (LNT) is treated as a loyalty point and for prepayment of services, and is not a security or currency. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For more on the Laconic Network Governance model and how it addresses the fundamental issues around asset classification, token usage, PoS networks and regulation, you can check out “" + }, + { + "url": "https://www.laconic.com/blog/laconic-governance-model", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "A New Governance Model for the New Web" + } + ] + }, + { + "type": "span", + "value": ".” " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Crypto isn’t going anywhere, but we will need to build safer and more decentralized systems, and work within a more aggressive regulatory environment. " + } + ] + } + ] + } + } + }, + "featured": false, + "id": "63853186", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/what-is-a-proof.json b/json/site_content/blogPost/what-is-a-proof.json new file mode 100644 index 0000000..43b08f4 --- /dev/null +++ b/json/site_content/blogPost/what-is-a-proof.json @@ -0,0 +1,1492 @@ +{ + "data": { + "blogPost": { + "slug": "what-is-a-proof", + "title": "What Is a Proof and Why Do You Need One?", + "date": "2022-09-20", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + }, + { + "id": "3545003", + "slug": "product", + "title": "Product" + } + ], + "author": { + "id": "55512259", + "name": "Stefan Adolf" + }, + "image": { + "url": "/images/site_content/blogPost/what-is-a-proof.png" + }, + "content": { + "blocks": [ + { + "id": "55510966", + "title": "Merkle tree" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralized apps rely heavily on blockchain state to display current and historical values like an account's balance, its NFT holdings, or current exchange rates on DEXes. Projects like The Graph or Covalent make blockchain data accessible, chain indexers like Etherscan provide historical information about the chain's state, and RPC providers like Infura or Alchemy allow wallet extensions like Metamask to connect to their full-node infrastructure. However, unless you run your very own full-node you cannot prove that any value provided by these sources truly resembles the chain state they represent. In short: relying on external, centralized services imposes a trust risk. However, the way Ethereum stores data allows trusting the chain state data without trusting the provider itself, thanks to proofs. This is how it works." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Ethereum's storage model" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ethereum uses a tree-based storage model that contains each account's balance and the storage for contract-based accounts. All values are wrapped in a traversable hash trie structure - a " + }, + { + "url": "https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Patricia Merkle Trie" + } + ] + }, + { + "type": "span", + "value": " - that allows creating cryptographic hash proofs for each tree node. The state root hash of a block represents the complete state of all accounts and contracts at a given block height, aka the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "world" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "state" + }, + { + "type": "span", + "value": ". Block producers execute new transactions on their local state copy and bundle the resulting new state root hash and a list of all executed transactions into a new block." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Light Ethereum clients cannot verify the current chain's state since they're not keeping a full copy of the world state. Full nodes can rebuild the state tree by executing every transaction since genesis, and then constantly update it as new blocks arrive. To save storage space they only keep the most recent chain states and hence cannot respond to state queries from the past, i.e. they're not able to determine an account's balance older than - depending on the node's settings - 64 blocks. That's sufficient to create proofs about the current state, though." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Cryptographic proofs on hash trees" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Merkle trees use hashes of their nodes' values as keys to guarantee their content integrity. To build a simple binary Merkle tree, a prover starts by computing a hash over a node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "L" + }, + { + "type": "span", + "value": "'s content " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": ". Next, they compute the hash over " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": " and the hash of a sibling content node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "K" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": ": " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(h(K)+h(L))" + }, + { + "type": "span", + "value": ". Using this hash as a key they create a new node " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "KL" + }, + { + "type": "span", + "value": " with " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(K)" + }, + { + "type": "span", + "value": " and " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(L)" + }, + { + "type": "span", + "value": " as children and find another node" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "IJ" + }, + { + "type": "span", + "value": " at the same tree level to create a new parent node that hashes all values of the underlying tree structure: " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "h(IJ + KL) = h(h(h(I) + h(J)) + h(h(K) + h(L)))" + }, + { + "type": "span", + "value": ". This process is repeated until all nodes are combined to a single root hash. Since each parent node keeps a hash of its children, it's impossible to change anything down the tree without affecting the tree's root node." + } + ] + }, + { + "item": "55510966", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove that a certain base value has been incorporated into a Merkle tree's root, one collects the hashes of sibling nodes at each level of the tree. A prover successively computes their hash sums to finally recreate the root hash, thereby proving the inclusion of the node's value." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "A simple way of storing binary Merkle trees in a flat key/value database structure is with " + }, + { + "url": "https://en.wikipedia.org/wiki/Radix_tree", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "radix tries" + } + ] + }, + { + "type": "span", + "value": ". To store a node, one splits its hexadecimal hash key into its nibbles (the hex characters) and create subdirectories for each of them. A node with the key " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "0xcafe" + }, + { + "type": "span", + "value": " would end up in a folder structure like " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "c/a/f/e/value" + }, + { + "type": "span", + "value": ". Looking up a node requires traversing the directory structure along the key's nibbles down to the last nibble's directory where the node's value will be stored. While simple to understand this approach is far too inefficient to store large tree data structures like Ethereum's world state. The network instead uses Patricia Merkle Tries that add some complexity to the data structure by introducing branch-, extension-, leaf- and null-nodes but are way more efficient to store and traverse. Additionally, a Recursive Length Prefix (" + }, + { + "url": "https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "RLP" + } + ] + }, + { + "type": "span", + "value": ") encoding is used to serialize nested arrays and data structures." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Proving account state" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Ethereum's state trie consists of a mapping between account addresses and their state, defined by their current " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "nonce" + }, + { + "type": "span", + "value": " and " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "balance" + }, + { + "type": "span", + "value": " and in the case of contract accounts a " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "storageRoot" + }, + { + "type": "span", + "value": " key that points to a position in the dedicated contract storage trie and their binary code hash. Just using a block's " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " one can look up an account's current state at that height using a full node's LevelDB (Geth's default storage database). Our examples are using an archive node of Ethereum's recently launched " + }, + { + "url": "https://sepolia.dev/", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Sepolia testnet" + } + ] + }, + { + "type": "span", + "value": " because it's still small enough to be synced to a developer's machine." + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst {\n toBuffer,\n bufferToHex,\n keccak256,\n Account,\n} = require(\"ethereumjs-util\");\nconst Web3 = require(\"web3\");\n\nconst web3 = new Web3(\"http://127.0.0.1:8545\");\nconst db = new Level(\".ethereum/sepolia/geth/chaindata\");\n\nasync function getState(address, blockNumber) {\n const block = await web3.eth.getBlock(blockNumber);\n const trie = new SecureTrie(db, toBuffer(block.stateRoot));\n\n //retrieves the account node's *value*\n const rawValue = await trie.get(toBuffer(address));\n const account = Account.fromRlpSerializedAccount(rawValue);\n console.log(account);\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "getState(\"0xe127a39da6ea2d7b1979372ae973a20bab08a80a\", 1432400)" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "value": "yields:" + } + ] + }, + { + "code": "account Account {\n nonce: ,\n balance: ,\n stateRoot: ,\n codeHash: \n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The provided account is an EOA, hence the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "codeHash" + }, + { + "type": "span", + "value": " value corresponds to " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "keccak256([])" + }, + { + "type": "span", + "value": " (another expression of a null value) and " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " is the hash of an RLP encoded zero:" + }, + { + "type": "span", + "marks": [ + "strong" + ], + "value": " " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "keccak256(rlp.encode(0))" + }, + { + "type": "span", + "value": "." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To create a proof of the account node's value against a block's state root you walk down the patricia trie starting at the block's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " node, resolve the paths on the trie's branch and extension nodes according to the signposts expressed by the leaf node's nibbles until the wanted leaf node is reached. On the way we're keeping track of all nodes we see while traversing the path. More details about " + }, + { + "url": "https://github.com/ethereumjs/ethereumjs-monorepo/blob/cfd7b7754490b072a035cceaba59c3dfb517effd/packages/trie/src/trie/trie.ts#L154", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "the traversal can be found here" + } + ] + }, + { + "type": "span", + "value": ". We're using only high level methods to show proof creation here:" + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst { toBuffer, keccak256 } = require(\"ethereumjs-util\");\n\nconst db = new Level(\".ethereum/sepolia/geth/chaindata\");\nconst trie = new SecureTrie(db, toBuffer(block.stateRoot));\n\n//create a proof by finding the path on our own:\nconst { node: accountNode, stack } = await trie.findPath(\n keccak256(toBuffer(address))\n);\nconst proof = stack.map((stackElem) => {\n return stackElem.serialize();\n});\n\n//or by using a trie's convenience method:\nconst proof = await SecureTrie.createProof(trie, toBuffer(address));\n\n//or by using the ethereum node's RPC interface\nconst { accountProof: proof } = await web3.eth.getProof(\n address,\n [0],\n block.number\n);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "All three proofs contain the same array of RLP encoded proof nodes, with our account node at its end:" + } + ] + }, + { + "code": "[\n \"0xf90211a078400dd6bd6f6cc13cdde5b707bdb6b9802fbd11809cba8aab2ee72f6920bbf3a0d88879eb6f43ea2d9c07538601e041aa46d64c87df68296a552ad12f8e5bebc0a044d40ac93c1bce308feda36a7712a38d9508087f04e612b1411bc57f80fa77a8a0b92c5fc0dcbc4a7982563c3667b940117770965e5f447578e4bde4fefcbb6d5aa0bc555b313ac90177e117dc10917581dad616770625e0956feb0df75a4be2ce63a0bd19008ae54cf1af36e59d7c92c553693dd468a0ef86c89401d01fc84aa4b751a0ee549b7ab8dd9a565ee6507bb46b9033e57b3a07541a4085763207911633367ca05652bd80f5b970a09c2090c38b13afa1772f3d97422c9a16548acdf629c35113a0a2e78f2d0da2d7218535b94bd69c4fad4425ff3bb22304d722185cf99dac2596a0a9470e771ae9cec5535b057948e68c4c38d8b8c1c2d8ed180d8f5244c63e422ea0726d10a2a5c85b156105414e75591611d896b8aaebd7fc92535845445a8baa81a0fdfb5aa2c35136eab67c3539d3ba34648bb8542b57e745976d52bca263ab19d7a01eafa4a1f59d28009a03b24d9a11c878faa78fe7ef3ecc4ef8891a77fea3fa35a02c980b0a841d5ec1978ae63adf7ba2727ac125f792f545cb0146d8df46adbdbfa052b97e4692fd69b4bebc6ceca258032319b07aca2ee62b504285031670fbc073a02c415e41b6bf838a8b742123fda110703ffdedea8834bb5113ae26f81887668e80\",\n \"0xf90211a098e0b7071d2b8d890cca479dba9f5b697d639d70d51f0312c5b8a27afc31a23da060759f93f482f66b233480a4cd060372567752b9dae507045b266600ab9b290da0c35bb5b2aae7eb8bc75a9de125daab9caa7d6d39e403153c9b34e51dcde46f78a097a4c1ba8ac42378481754eac9306b62d3d176602098820cb87e3c7f4091734da074b2785ca9199d0c68e2fa6dd01dcc24791887616adf8043cda56651cff25e87a00937963d57bc119144ebeff9bb99dcbef11aed563fd41818aaf0de89d15364b3a000ba5def8acd7c1c2af85ef49bc5d144fa1ceef93e81e9e64ad6457ab92cb569a069cd4b3ac6b03b1928f50840f81086ff0c819d1cbd445db0e5c306cfce9c0728a0a78339d0b9405da5a7943a6ac97b9585767a6f9f0e481b5e197d8fd820ac1cfaa019e1319ade8f1c2254dcef0b68483240044bc6bee2ea8197de895eb427068b1aa0ef36884df92b5769e521c6c0feaead503a8d70d11f493b3e6c8170ac917ced92a079c1ffdaa2a8d4ddacac77f47b43f1553c634300d23a8003574ec90874547cbaa0de4f130f1fd52baf5229e3871394b6f0562c64576039c6c38d06c695b7d87b07a09668798efa8c1e9b860d9f4a49b65db83c22fefd92f703a5875b87c7ae11bdf4a087ca8f145dbc9ad7f88468dbec0627d77ced63a508b4d7a5d28439e89c6be8e4a04fdb75be4fce4d76e41578f0bbcfdc2ded74d59855c9f3afc73c3659b9e28aa980\",\n \"0xf90211a058d99773cf489bd86bac2c6452c20cc7056f3505d7f96d83480cf85336d0da85a07dc4216585f73a213fb1828076cf7d93d8ce95e1981a5c8eb11d7166af8433eba0f85f31a95a1f83516719ae596266afdc529aa4ff9af8f0f6c1d7da6751f07d70a0f1371490b8ef31ce814673e9ecf0f498f2ec9eedcd554e93162d5c0e99e77d69a01d9709211cc089eba15fe3c052539203d11235b219e1c72e0172c9bec1d317bea09e907ad084233149719b5c6f8b4777a617b41744a0c3e9f6ef002970465a0656a044f3abc34546455ba2ed3d64ead41c734d5f56c240f8f8adfd971179538ca0efa08403716832691c8867b1366421b703925ff28e45d6f4360f898b70461e29d4eda0d8f4711ffe7bc79040b2f397523786a67d72814e8af9b65c9e03c318bb0eba48a0db84d2a268e67d6e45af7cd18a92fab1df496180e9ba8f0f0b620ab6344457cba0d01707094316206d689d80ed2684cda3aadd72ee2d4efbe1437bb194aa0adc87a0355e0e203341ac995bf2d90937f1ce807a13624e9d3831476f3205838b84f191a083961a9b498b791ee8e6a110a87aca2f2cea629df0827f4fbd168567d7246207a05d312338764151dc1936485637367ad9d9c440b4b0dde368dec4dbd5aaf51f4ea076b77567690221701e2edfff06f1c84a25b3181e4c8aa3bed2b3e3797a883564a0975b63f1be5615a4a68ec7f313abc0784def11a352975c40e886e03df95f764e80\",\n \"0xf9013180a0944968a4a8c7ba1e91cbb2414471a67f16c74e2ef6ac87447af0402520aed9c88080a03f3e1fed1008e242b89ce5b56106a091f946826c539f23ecee74c5b36b5d38cca041b58c41b5435e10c9b9f6b1f1bdf3d5e0294dfcfbf59b7a8c80475249e1d9e6a0d0b42862dcb175e47bf17614bd251b17cdab2710ba13175fd9b8c17afdc0893ba0ff4dc7ee613a7805802e50d39c3c8d8b35db63ceda7371502c5d119eca0a7b1780a0cd328766d44f930edc3a32ae5fd2a791f0de4dbabe014c4be77fc63b4b427310a0c20a6a1812f18d0c446408f90b09d0b592a07c45d06e780b49c6ad68e7c92bc5a084688c931305a75246f9d527a07483cff6e9ffbe4746a22ca65e07d3b3c6045f8080a0515f82b6e34b4713edead2652f266fdcea3729d64217113db564e918a9b1ed408080\",\n \"0xf8518080a06b91f274ef06ab455966416f6bb3779519bf72c68e85ec4b61ff8b99aa73a1818080a0937ee4540dc495eaa3cd61e12a4f6158a6be147d71ac3955a50ecd080e9732698080808080808080808080\",\n \"0xf8709e3e0484bbc22108bc77412e65255ce0387dc7c6a8d1917625ddb60ccbd98fb84ff84d028901f3d52b3c4f92e45da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"\n]", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To verify this proof one doesn't need to have access to a full state tree at that block height. It's sufficient to know (and trust, e.g. by running a light node) the proof block's " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "const { SecureTrie } = require(\"merkle-patricia-tree\");\nconst { toBuffer, keccak256, Account } = require(\"ethereumjs-util\");\n\n// look Ma, no ethereum node needed\n// @param stateRoot hexString: the block's stateRoot\nasync function verifyAccountProof(address, proof, stateRoot) {\n const proofBufs = proof.map((p) => toBuffer(p));\n //build a new trie using only the proof nodes. They will hash towards the block's stateRoot\n const proofTrie = await SecureTrie.fromProof(proofBufs);\n\n //the account node can also be retrieved from the partial trie:\n const accNodeRaw = await proofTrie.get(keccak256(toBuffer(address)));\n const account = Account.fromRlpSerializedAccount(accNodeRaw);\n\n console.log(\"proven value\", account);\n const valid = await proofTrie.checkRoot(toBuffer(stateRoot));\n //or: const valid = bufferToHex(proofTrie.root) == stateRoot;\n return valid;\n}", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Proving contract state" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Proofs over contract state are created accordingly. They are rooted at the contract account's own " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "stateRoot" + }, + { + "type": "span", + "value": " member variable that points to the mutable root of the contract's storage trie. Storage slots are addressed by their position hash as outlined in the " + }, + { + "url": "https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html#mappings-and-dynamic-arrays", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Solidity documentation" + } + ] + }, + { + "type": "span", + "value": " and are stored as leaf nodes in the storage trie. To simplify this procedure, we're going to use " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "eth_getProof" + }, + { + "type": "span", + "value": " calls here as described in " + }, + { + "url": "https://eips.ethereum.org/EIPS/eip-1186", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "EIP-1186" + } + ] + }, + { + "type": "span", + "value": " and supported by all major chain node implementations and service providers. Lets prove " + }, + { + "url": "https://sepolia.etherscan.io/address/0xF492600AeD292b1B94A1ba0CD29fB6ed6d6ab872", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "this contract" + } + ] + }, + { + "type": "span", + "value": "'s " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "greeting" + }, + { + "type": "span", + "value": " to be \"" + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "Hello, Hardhat!" + }, + { + "type": "span", + "value": "\" at Sepolia block number " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "1391140 " + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "pragma solidity ^0.8.0;\n\ncontract Greeter {\n string private greeting;\n\n constructor(string memory _greeting) {\n greeting = _greeting;\n }\n\n function setGreeting(string memory _greeting) public {\n greeting = _greeting;\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Now, lets create a proof of its storage slot 0 (the string variable " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "greeting" + }, + { + "type": "span", + "value": " ):" + } + ] + }, + { + "code": "const { bufferToHex } = require(\"ethereumjs-util\");\nconst { SecureTrie } = require(\"merkle-patricia-tree\");\nconst Web3 = require(\"web3\");\n\nconst web3 = new Web3(\"http://127.0.0.1:8545\");\n\nconst proveContractStorage = async (blockNumber) => {\n const proof = await web3.eth.getProof(\n \"0xf492600aed292b1b94a1ba0cd29fb6ed6d6ab872\", //the contract's address on Sepolia\n [0], //the first storage slot\n blockNumber\n );\n console.log(proof.storageProof);\n const value = web3.utils.hexToAscii(proof.storageProof[0].value);\n console.log(value);\n\n const proofBufs = proof.storageProof[0].proof.map(toBuffer);\n const proofTrie = await SecureTrie.fromProof(proofBufs);\n const valid = bufferToHex(proofTrie.root) == proof.storageHash;\n console.log(valid);\n};", + "type": "code" + }, + { + "code": "[\n {\n key: '0x0',\n value: '0x48656c6c6f2c204861726468617421000000000000000000000000000000001e',\n proof: [\n '0xf8518080a0e2a22c03c4f21673563d55cbad16de4c4affc9a54c3eea063ce358ccd6d02c4c8080808080808080a0fc47ec7aea920817d850c758a8fd61ecc89b967b2fe8d9ec6d00feedeb0a7d658080808080',\n '0xf843a0390decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563a1a048656c6c6f2c204861726468617421000000000000000000000000000000001e'\n ]\n }\n]\nHello, Hardhat!\ntrue", + "type": "code" + }, + { + "type": "heading", + "level": 3, + "children": [ + { + "type": "span", + "value": "A non trivial example" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Creating proofs over nested structures, dynamic types like strings, or deeply buried storage slots is slightly harder because you have to be familiar with Solidity's " + }, + { + "url": "https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "storage layout" + } + ] + }, + { + "type": "span", + "value": ". To demonstrate that, here is a non-trivial example to prove that Jimmy Fallon owned " + }, + { + "url": "https://opensea.io/assets/ethereum/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d/599", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Bored Ape #599" + } + ] + }, + { + "type": "span", + "value": " at block height " + }, + { + "url": "https://etherscan.io/block/13572667", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "13572667" + } + ] + }, + { + "type": "span", + "value": " as he claimed during his Tonight Show " + }, + { + "url": "https://www.youtube.com/watch?v=5zi12wrh5So&t=222s", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "in January 22" + } + ] + }, + { + "type": "span", + "value": ". The Bored Apes NFT contract inherits from OpenZeppelin's legacy V3 " + }, + { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.2/contracts/token/ERC721/ERC721.sol#L36", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "enumerable ERC721 contract" + } + ] + }, + { + "type": "span", + "value": ". The data structure storing ownership information about a single token is the private " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "_tokenOwners" + }, + { + "type": "span", + "value": " member struct that maps a token id to an index onto another dynamic array of key value mappings." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove the ownership storage values we need to take a look at the contract's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "ownerOf" + }, + { + "type": "span", + "value": " implementation and its related structs. Here's a summary of the relevant parts of the " + }, + { + "url": "https://etherscan.deth.net/address/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d#code", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "full BAYC code base" + } + ] + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "library EnumerableMap {\n struct MapEntry {\n bytes32 _key;\n bytes32 _value;\n }\n\n struct Map {\n MapEntry[] _entries;\n // Position of the entry defined by a key in the `entries` array, plus 1\n // because index 0 means a key is not in the map.\n mapping (bytes32 => uint256) _indexes;\n }\n\n struct UintToAddressMap {\n Map _inner;\n }\n\n function _get(Map storage map, bytes32 key) private view returns (bytes32) {\n uint256 keyIndex = map._indexes[key];\n require(keyIndex != 0, \"EnumerableMap: nonexistent key\"); // Equivalent to contains(map, key)\n return map._entries[keyIndex - 1]._value; // All indexes are 1-based\n }\n\n function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {\n return address(uint160(uint256(_get(map._inner, bytes32(key)))));\n }\n //...\n}\n\ncontract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {\n\n // Mapping from holder address to their (enumerable) set of owned tokens\n mapping (address => EnumerableSet.UintSet) private _holderTokens;\n\n // Enumerable mapping from token ids to their owners\n EnumerableMap.UintToAddressMap private _tokenOwners;\n\n function ownerOf(uint256 tokenId) public view virtual override returns (address) {\n return _tokenOwners.get(tokenId, \"ERC721: owner query for nonexistent token\");\n }\n //...\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The index mapping " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "_tokenOwners" + }, + { + "type": "span", + "value": " is the contract's third storage member (the 1st one being part of the ERC-165 implementation) and it implicitly points to the " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "Map" + }, + { + "type": "span", + "value": " struct via " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "UintToAddressMap.inner " + }, + { + "type": "span", + "value": ", occupying two storage slots. By applying Solidity's " + }, + { + "url": "https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "layout rules for dynamic arrays" + } + ] + }, + { + "type": "span", + "value": ", the storage slot's address of the enumeration index that points to the current owner of token #599 can be computed as " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "const indexSlot = web3.utils.soliditySha3(599, 3);" + }, + { + "type": "span", + "value": ". Querying that storage slot's value by " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "web3.eth.getStorageAt(baycAddress, indexSlot, blockNumber)" + }, + { + "type": "span", + "value": " yields the index " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "0x258" + }, + { + "type": "span", + "value": " at the given block height. Considering that the " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "Map._entries" + }, + { + "type": "span", + "value": " array starts at the contract's third slot, it takes two slots to store one " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "MapEntry" + }, + { + "type": "span", + "value": " and the indexes are 1-based we can resolve the map's " + }, + { + "type": "span", + "marks": [ + "emphasis", + "strong" + ], + "value": "_value" + }, + { + "type": "span", + "value": " member that carries the token owner's address like so: " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "code" + ], + "value": "soliditySha3(2) + (0x258 * 2) - 2 + 1" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "With that we can construct two storage proofs for the index and the owner's address:" + } + ] + }, + { + "code": "const baycAddress = \"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d\";\nconst blockNumber = 13572667;\n\nconst indexSlot = web3.utils.soliditySha3(599, 3);\n// -> 0x7e2616eb7a75f68a32624f502cf2cabc166c302900bbdc790c2fb85cea316a21\nconst indexValue = await web3.eth.getStorageAt(\n baycAddress,\n indexSlot,\n blockNumber\n); //0x258\n\nconst bnIndex = ethers.BigNumber.from(indexValue);\nconst valueSlot = ethers.BigNumber.from(web3.utils.soliditySha3(2))\n .add(bnIndex.mul(2))\n .sub(2)\n .add(1);\n// -> 0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5f7d\n\nconst proof = await web3.eth.getProof(\n baycAddress,\n [indexSlot, valueSlot],\n blockNumber\n);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To prove the " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "value" + }, + { + "type": "span", + "value": " of the yielded proof, we can:" + } + ] + }, + { + "code": "async function validateStorage(proof, slot, proofIdx) {\n const proofBufs = proof.storageProof[proofIdx].proof.map(toBuffer);\n const pTrie = await SecureTrie.fromProof(proofBufs);\n const valid = pTrie.checkRoot(toBuffer(proof.storageHash));\n const rlpNode = await pTrie.get(toBuffer(web3.utils.keccak256(slot)));\n console.log(\"content at slot\", slot, bufferToHex(rlp.decode(rlpNode)));\n return valid;\n}\n\nconsole.log(await validateStorage(proof, indexSlot, 0));\nconsole.log(await validateStorage(proof, valueSlot.toHexString(), 1));", + "type": "code" + }, + { + "code": "content at slot 0x9f82913e56c1ea296cd5a3c46bc89a4073098f41767359e4c3742445923985c7 0x0258\ntrue\ncontent at slot 0xd4790f3899b463e8194660196b97cf3ff9c47008d83ca7c0e51ce406d3c784e5 \\\n0x0394451c1238cec1e825229e692aa9e428c107d8 (<- Jimmy Fallon's address)\ntrue", + "type": "code" + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "value": "Validating proofs inside smart contracts" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These have been client side examples, but proofs can also be validated inside Solidity contracts, e.g. to prove historical chain information that's not available to the contract itself. A good example can be seen at Lido Finance's " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "trustless ETH/stETH price pool oracles" + } + ] + }, + { + "type": "span", + "value": " that receives price reports as a combination of block header, account and state proofs. Since proofs are submitted " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle/blob/main/contracts/StableSwapStateOracle.sol#L295", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "as memory variables" + } + ] + }, + { + "type": "span", + "value": ", price updates " + }, + { + "url": "https://etherscan.io/tx/0xb24e20813e08f75e12e29da53f7f6c7e5dca7be68f3d3247edd4de572c527df4", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "are relatively cheap" + } + ] + }, + { + "type": "span", + "value": ". Here's a contract that's built on " + }, + { + "url": "https://github.com/lidofinance/curve-merkle-oracle/tree/main/contracts", + "meta": [ + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "Lido's primitives" + } + ] + }, + { + "type": "span", + "value": " and verifies any given state proof. It's also " + }, + { + "url": "https://goerli.etherscan.io/address/0x52e357f616a13089435be73e20cffb788eb4c928#code", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "deployed on Görli" + } + ] + }, + { + "type": "span", + "value": ":" + } + ] + }, + { + "code": "// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\npragma experimental ABIEncoderV2;\n\nimport \"./StateProofVerifier.sol\";\nimport {RLPReader} from \"solidity-rlp/contracts/RLPReader.sol\";\n\ncontract ProofVerifier {\n using RLPReader for bytes;\n using RLPReader for RLPReader.RLPItem;\n using StateProofVerifier for StateProofVerifier.Account;\n\n /*\n struct Account {\n bool exists;\n uint256 nonce;\n uint256 balance;\n bytes32 storageRoot;\n bytes32 codeHash;\n }\n\n struct SlotValue {\n bool exists;\n uint256 value;\n }\n */\n\n function extractAccountFromProof(\n address _address,\n bytes32 _stateRootHash,\n bytes memory _proofRlpBytes\n ) public pure returns (StateProofVerifier.Account memory account) {\n RLPReader.RLPItem[] memory proofs = _proofRlpBytes.toRlpItem().toList();\n bytes32 addressHash = keccak256(abi.encodePacked(_address));\n\n account = StateProofVerifier.extractAccountFromProof(\n addressHash,\n _stateRootHash,\n proofs\n );\n }\n\n function extractSlotValueFromProof(\n bytes32 _slotHash,\n bytes32 _storageRootHash,\n bytes memory _proofRlpBytes\n ) public pure returns (StateProofVerifier.SlotValue memory slotValue) {\n RLPReader.RLPItem[] memory proofs = _proofRlpBytes.toRlpItem().toList();\n slotValue = StateProofVerifier.extractSlotValueFromProof(\n _slotHash,\n _storageRootHash,\n proofs\n );\n }\n}", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Continuing with the proof of Jimmy Fallon's Bored Ape, this is how you would prepare RLP encoded versions of the proof's node array as required by the contract's method interface:" + } + ] + }, + { + "code": "//converts an array of rlp encoded proofs to an rlp encoded array of proofs.\nconst rlpEncodeProof = (proof) => {\n const rlpDecodedProofs = proof.map((p) => rlp.decode(toBuffer(p)));\n return rlp.encode(rlpDecodedProofs);\n};\n\nconst rlpAccountProof = rlpEncodeProof(proof.accountProof);\nconst indexStorageProof = rlpEncodeProof(proof.storageProof[0].proof);\nconst valueStorageProof = rlpEncodeProof(proof.storageProof[1].proof);", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "and submit it to the contract:" + } + ] + }, + { + "code": "//state root of mainnet block #13572667\nconst blockStateRoot =\n \"0xa710dad6c716e0b762a671865cbe0d286f158198580f4ac97c4ace95ea85ba1b\";\nconst baycAddress = \"0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d\";\n\nasync function main() {\n const Verifier = await ethers.getContractFactory(\"ProofVerifier\");\n // 0x52E357F616a13089435bE73E20CFfB788Eb4C928 on Görli:\n const verifier = Verifier.attach(process.env.CONTRACT_VERIFIER);\n\n //\"account\" is the bored apes contract\n const accountResult = await verifier.extractAccountFromProof(\n baycAddress,\n blockStateRoot,\n rlpAccountProof\n );\n const indexResult = await verifier.extractSlotValueFromProof(\n ethers.utils.keccak256(indexSlot),\n accountResult.storageRoot,\n rlpIndexProof\n );\n const valueResult = await verifier.extractSlotValueFromProof(\n ethers.utils.keccak256(valueSlot),\n accountResult.storageRoot,\n rlpValueProof\n );\n\n console.log(\n accountResult,\n indexResult.value.toHexString(),\n valueResult.value.toHexString()\n );\n}", + "type": "code" + }, + { + "code": "[\n exists: true,\n nonce: BigNumber { value: \"1\" },\n balance: BigNumber { value: \"0\" },\n storageRoot: '0x3f99b7df7989c11417c18b517c333ec74104e23ae76a50d578292ba3d466d77d',\n codeHash: '0x0ba5e25e74d81bab327110c8d8b44320f50ad5c3e91a546a5c5a9b605cf653b3'\n]\n0x0258\n0x0394451c1238cec1e825229e692aa9e428c107d8 // <- Jimmy Fallon, again.", + "type": "code" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Creating " + }, + { + "url": "https://eips.ethereum.org/EIPS/eip-1186", + "meta": [ + { + "id": "rel", + "value": "nofollow" + }, + { + "id": "target", + "value": "_blank" + } + ], + "type": "link", + "children": [ + { + "type": "span", + "value": "EIP-1186" + } + ] + }, + { + "type": "span", + "value": " compatible historical cryptographic proofs as demonstrated is a rather demanding task. It requires provers to traverse their archive nodes' LevelDBs, requiring up to 16 disk operations per account and storage slot. However, since state proofs like Jimmy Fallon's Bored Ape ownership at block height 13572667 are deterministic and valid forever, one could presciently collect all of the hashes along the nodes from root of the state tree down to specific nodes and index them. Chain indexers or RPC relayers like Metamask could use those hashes to execute proofs, thus minimizing trust in the service itself: each reply would be provable by the client." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Laconic Network greatly simplifies the process of generating proofs for blockchain data, saving time for developers, and avoiding the extensive recursive querying of full-node databases that would otherwise be required." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "55510965", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/blogPost/why-decentralization-matters.json b/json/site_content/blogPost/why-decentralization-matters.json new file mode 100644 index 0000000..deda9ee --- /dev/null +++ b/json/site_content/blogPost/why-decentralization-matters.json @@ -0,0 +1,671 @@ +{ + "data": { + "blogPost": { + "slug": "why-decentralization-matters", + "title": "Why Decentralization Matters ", + "date": "2022-11-22", + "category": [ + { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + ], + "author": { + "id": "55641250", + "name": "Joshua Eustis" + }, + "image": { + "url": "/images/site_content/blogPost/why-decentralization-matters.png" + }, + "content": { + "blocks": [ + { + "id": "61461493", + "title": "Blockchain Trilemma" + } + ], + "links": [], + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The emergence of any technology brings with it new language, and changes or expands the possible meanings of existing terms. Blockchain, of course, is no exception—and almost any discussion of it will include lots of talk about decentralization. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We should probably start with a definition. In the context of blockchain, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralization" + }, + { + "type": "span", + "value": " denotes the transfer of ownership and governance to the many from the few. Think of the differences between Prince, a solo songwriter and performer who insisted on absolute and final control over his artistic output, and the Beatles, a band that (despite their well-known internal power struggles) had no designated “lead” singer or sole songwriter. While decentralization may not require absolute accord, it does locate power across a system rather than in one designated point. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "It’s worth noting that many in the Web3 world use the terms " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralized " + }, + { + "type": "span", + "value": "and " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distributed" + }, + { + "type": "span", + "value": " interchangeably" + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": ", " + }, + { + "type": "span", + "value": "but this is technically incorrect. In the context of the blockchain, " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distributed" + }, + { + "type": "span", + "value": " refers not to ownership or governance, but only to the distribution of parties (in most cases, servers) across a physical or digital space.\n\nIn this context, decentralization isn't a binary value. Instead, it describes the values and characteristics of a system that deliberately locates significant aspects of power across a defined system, rather than at a single point within it. " + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "But, like, what does decentralization " + }, + { + "type": "span", + "marks": [ + "strong", + "emphasis" + ], + "value": "do?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is one of the primary innovations that powers blockchains. As the number of parties participating in the consensus mechanism of a blockchain increases, so does its level of decentralization. Participants' consensus about a given digital truth replaces the need for individual trust agreements, while establishing credible censorship resistance for anyone who wants to " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "do something" + }, + { + "type": "span", + "value": " on that blockchain. Any transaction is agreed upon by all, which allows any two parties to transact without first establishing a specific shared trust. This removes the need for oversight by a third party—for example, a \"financial panopticon\" established to prevent fraud—and reduces associated privacy and security risk. " + } + ] + }, + { + "item": "61461493", + "type": "block" + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization plays a key role in the Blockchain Trilemma—the need to balance decentralization, security, and scalability. A blockchain's level of decentralization is directly proportional to its ability to withstand attacks from inside or outside the network, and directly correlated with its level of neutrality, with greater decentralization linked to stronger censorship resistance and spam-resistant pricing. " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most exciting things about decentralization is that the digital truth agreed upon via consensus may be proven by anyone running a node. Network participants must uphold the established consensus when making new blocks, or their stake will be slashed—they'll lose a significant portion of the benefits of entry. And while block producers are referred to as " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "validators" + }, + { + "type": "span", + "value": ", they perform no defined validation action. Instead, they have a financial incentive to act upon the truth determined by a decentralized quorum of participants." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As the number of participants in a blockchain increases, so does the power of decentralization, preventing participants from stealing someone else’s assets by manipulating transactions submitted for block inclusion. Each member controls the private key for a specific address, so no one can censor or manipulate a transaction once it’s been submitted. In the broadest sense, this system operates counter to centralized payment protocols such as PayPal, which censors transactions and even " + }, + { + "url": "https://www.forbes.com/sites/emilymason/2022/10/27/after-paypal-revokes-controversial-misinformation-policy-major-concerns-remain-over-2500-fine/?sh=2e5a914c30c4", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "levies fines on users for not obeying a set of internally derived rules." + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Until recently, says Laconic cofounder and chief protocol designer Rick Dudley, “we’ve mostly been concerned with usurious intermediaries. So maybe it’s best to think about " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "disintermediation" + }, + { + "type": "span", + "value": " as a primary goal within a decentralized system.\" Profit-driven intermediation, he notes, \"is also far more difficult to add to a well-decentralized system, which resists usury by nature.”" + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Look, I don’t care about jpegs or digital Monopoly money. What’s in it for me?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization doesn't just increase the security of financial assets ... or ape GIFs No one party controls the decentralized network, so " + }, + { + "url": "https://www.coindesk.com/business/2022/06/29/coinbase-is-reportedly-selling-geo-location-data-to-ice/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "your pedigree information can't be given or sold to corporations, governments, or any other third parties without your consent" + } + ] + }, + { + "type": "span", + "value": ", as is dismayingly common on Web2 platforms. Decentralization can also protect network members from arbitrary moralities of a market subset—think the ejection of sex workers from Craigslist, protesters losing their financial platforms under political pressure, or citizens prevented from transacting by their government." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Anything else?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization also maximizes data reliability. A single point of failure can affect huge swaths of the public; imagine the effects of destroying the only server farm servicing a specific bank. Of course, to mitigate the risks associated with power outages, simple server failures, or even terrorist attacks, banks geographically " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "distribute" + }, + { + "type": "span", + "value": " their servers, with multiple parties storing multiple copies of data in multiple places across a " + }, + { + "type": "span", + "marks": [ + "emphasis" + ], + "value": "decentralized" + }, + { + "type": "span", + "value": " network. " + }, + { + "url": "https://en.wikipedia.org/wiki/Byzantine_fault", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Even if a large portion of its participants were to go dark, the network would still function." + }, + { + "type": "span", + "value": "\n\n" + } + ] + }, + { + "type": "span", + "value": "In this scenario, the bank is protected—though you, as a user of the bank, are vulnerable to having your transactions watched, censored, or thrown out altogether. The contents of your account can also be " + }, + { + "url": "https://ij.org/report/policing-for-profit-2/grading-state-federal-civil-forfeiture-laws/irs-cleans-out-bank-accounts/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "seized for potentially arbitrary or extralegal reasons" + } + ] + }, + { + "type": "span", + "value": ". As the recent " + }, + { + "url": "https://cointelegraph.com/news/bitcoin-sinks-to-new-yearly-low-at-16-8k-as-ftx-insolvency-fears-turn-into-contagion", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "FTX meltdown" + } + ] + }, + { + "type": "span", + "value": " made all too clear, centralized cryptocurrency exchanges are even more precarious—and because of their inherent lack of regulation, " + }, + { + "url": "https://www.forbes.com/sites/haileylennon/2022/08/01/bankrupt-crypto-lender-celsius-could-leave-customers-last-in-line-to-get-paid/?sh=4c4005ea5fde", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "prone to leave retail investors holding the bag" + } + ] + }, + { + "type": "span", + "value": ". A more decentralized system could offer individual users far more protection against institutional controls, not to mention far less potential for reckless mismanagement by those holding the keys." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Still a long way to go" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralizing a protocol or network still poses many challenges. Foremost among them is the fact that " + }, + { + "url": "https://www.weforum.org/agenda/2016/05/joseph-stiglitz-are-markets-efficient-or-do-they-tend-towards-monopoly-the-verdict-is-in/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "markets tend to direct capital to the few and not the many" + } + ] + }, + { + "type": "span", + "value": ". Our current way of life is undergirded by a system that counters decentralization at every level, moving power away from the majority. Accepting decentralized systems in the physical world would require changing much of how we think about how society is arranged, and about " + }, + { + "url": "https://www.latimes.com/opinion/op-ed/la-oe-pascrell-live-nation-concert-ticketing-20180517-story.html", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "rapidly shrinking" + } + ] + }, + { + "type": "span", + "value": " access to competition under late capitalism." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Another key point: Governments that cannot monitor private transactions often attempt to sanction users of protocols that protect the anonymity afforded by robust, decentralized censorship resistance. Such moves can be used to" + }, + { + "url": "https://home.treasury.gov/policy-issues/financial-sanctions/sanctions-programs-and-country-information/iran-sanctions", + "type": "link", + "children": [ + { + "type": "span", + "value": " " + }, + { + "type": "span", + "marks": [ + "underline" + ], + "value": "uphold international sanctions" + } + ] + }, + { + "type": "span", + "value": "—or " + }, + { + "url": "https://www.wsj.com/articles/crypto-advocacy-group-sues-u-s-treasury-over-tornado-cash-sanctions-11665610506?mod=article_inline", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "oppress a government’s own citizenry" + } + ] + }, + { + "type": "span", + "value": ". " + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "One of the most glaring problems of decentralization is Web3 spaces' level of volatility. While money laundering has existed since the beginning of money (and would continue even if all the blockchains in the world were to vanish), there's enough money laundering, wash trading, and socially engineered theft on blockchain networks to lead regulators to sanction, " + }, + { + "url": "https://www.coindesk.com/policy/2022/08/21/arrest-of-tornado-cash-developer-draws-dutch-crypto-community-protest/", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "or even imprison" + } + ] + }, + { + "type": "span", + "value": ", participants and even developers—while hinders true decentralization across the ecosystem. If we want to make the ecosystem safe for everyone, we've got a lot of housecleaning to do." + } + ] + }, + { + "type": "heading", + "level": 2, + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "So where does Laconic come in?" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is a core feature of the Laconic Network, delivering all of the benefits we've described—uptime, security, affordability, censorship resistance, and fair, consensus-driven governance. The network's seven founding Members, and 60+ additional Members spread across multiple jurisdictions, each run their own hardware, making it highly resistant to concentrated server failures, tampering by individual bad actors, and hostile takeovers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Each Member serves the network by running " + }, + { + "url": "https://www.laconic.com/blog/laconic-watchers", + "type": "link", + "children": [ + { + "type": "span", + "marks": [ + "underline" + ], + "value": "Watchers," + } + ] + }, + { + "type": "span", + "value": " smaller caches of specific data commonly used by, for example, DApp developers. There's no single point of failure—if any one Service Provider fails, other Members can ensure Watcher uptime." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization will drive even more benefits as the network matures. Laconic Network customers can purchase data directly from Service Providers, which compete to win customers through low prices, at the same time exerting downward pressure on data prices across the industry." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Finally, while geographically diverse traditional networks can be subject to an incredibly complex web of national and local laws, participants in the decentralized Laconic Network need comply only with those laws governing their own jurisdictions. “In each jurisdiction,\" explains Dudley, \"varying and often contradictory regulations or sanctions can interfere with or contradict one another, making global compliance difficult. The goal is to be compliant in our jurisdiction, while allowing our users to be compliant in each of theirs.” Each Laconic Stack user decides how best to comply with the laws of their jurisdiction. The biggest benefit here, though, is that under this model, no single governing body has the power to determine a specific flow of data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Decentralization is at the heart of Web3, and key to all major benefits of the blockchain. As a structure, it has the power to offer the most benefit to the largest group of people, making it more difficult for the few to hoard power or capital at the expense of the many. But for blockchain, and the larger Web3 ecosystem, to reach its full potential, we must work toward broad understanding of the benefits and pitfalls of decentralized networks. Only then can we begin to calm the waters of Web3, and remove the sharks for the next billion users we hope to bring with us." + } + ] + } + ] + } + } + }, + "featured": true, + "id": "61461416", + "_status": "published", + "_seoMetaTags": [ + { + "attributes": null, + "content": "Blog Post Title", + "tag": "title" + }, + { + "attributes": { + "property": "og:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:title", + "content": "Blog Post Title" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:image", + "content": "/images/site_content/blogPost/blog-post-slug.png?auto=format&fit=max&w=1200" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:locale", + "content": "en" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "og:type", + "content": "article" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "property": "article:modified_time", + "content": "1999-09-09T00:00:00Z" + }, + "content": null, + "tag": "meta" + }, + { + "attributes": { + "name": "twitter:card", + "content": "summary" + }, + "content": null, + "tag": "meta" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/careersPage.json b/json/site_content/careersPage.json new file mode 100644 index 0000000..837a7ce --- /dev/null +++ b/json/site_content/careersPage.json @@ -0,0 +1,73 @@ +{ + "data": { + "careersPage": { + "heroHeading": "Build the Future Now", + "heroLine": "Join Laconic to build a decentralized future.", + "valuesHeading": "Values", + "valuesPoint01Head": "Precision", + "valuesPoint01Image": { + "url": "/images/site_content/careersPage/valuesPoint01Image.png" + }, + "valuesPoint01ImageLight": { + "url": "/images/site_content/careersPage/valuesPoint01ImageLight.png" + }, + "valuesPoint01Line": "The quality, condition or fact of being exact and accurate", + "valuesPoint02Head": "Elegance", + "valuesPoint02Image": { + "url": "/images/site_content/careersPage/valuesPoint02Image.png" + }, + "valuesPoint02ImageLight": { + "url": "/images/site_content/careersPage/valuesPoint02ImageLight.png" + }, + "valuesPoint02Line": "The quality of being pleasingly ingenious and simple", + "valuesPoint03Head": "Self-Sovereignty", + "valuesPoint03Image": { + "url": "/images/site_content/careersPage/valuesPoint03Image.png" + }, + "valuesPoint03ImageLight": { + "url": "/images/site_content/careersPage/valuesPoint03ImageLight.png" + }, + "valuesPoint03Line": "The quality of controlling one’s own accounts and data", + "valuesPoint04Head": "Curiosity", + "valuesPoint04Image": { + "url": "/images/site_content/careersPage/valuesPoint04Image.png" + }, + "valuesPoint04ImageLight": { + "url": "/images/site_content/careersPage/valuesPoint04ImageLight.png" + }, + "valuesPoint04Line": "A strong desire to know or learn something", + "whyHeading": "Why Join Laconic Network?", + "whyPoint01Img": { + "url": "/images/site_content/careersPage/whyPoint01Img.png" + }, + "whyPoint01Line": "Build the future now", + "whyPoint02Img": { + "url": "/images/site_content/careersPage/whyPoint02Img.png" + }, + "whyPoint02Line": "Solve big challenges", + "whyPoint03Img": { + "url": "/images/site_content/careersPage/whyPoint03Img.png" + }, + "whyPoint03Line": "Work around the world", + "whyPoint04Img": { + "url": "/images/site_content/careersPage/whyPoint04Img.png" + }, + "whyPoint04Line": "Fair and equitable environment", + "whyPoint05Img": { + "url": "/images/site_content/careersPage/whyPoint05Img.png" + }, + "whyPoint05Line": "Growth opportunities", + "whyPoint06Img": { + "url": "/images/site_content/careersPage/whyPoint06Img.png" + }, + "whyPoint06Line": "Freedom and flexibility", + "whyHeadNumber": "06", + "positionsHeading": "Open Positions", + "positionsHeadingNumber": "06", + "positionsLinkLabel": "Apply now", + "positionsXylmHead": "Xylm", + "positionsDeepstackHead": "Deep Stack", + "positionsCercHead": "Cerc" + } + } +} \ No newline at end of file diff --git a/json/site_content/category/__readme.txt b/json/site_content/category/__readme.txt new file mode 100644 index 0000000..f391c28 --- /dev/null +++ b/json/site_content/category/__readme.txt @@ -0,0 +1,7 @@ +========NEXT_PUBLIC_DATOCMS_BYPASS /json/site_content/category/_readme.txt +-The JSON files in this directory were added for completion when migrating away from datocms +-To my knowledge this specific node is a dead end that is not linked to or iterated in any way +-That is to say, there is no linking out to a specific 'category' page +-The links to specific categories on the blog home page were removed, as they were not even working prior to migration +-The node(s) only exists in the /json/site_content/author/blogPosts JSON files +-The nodes are then scraped FROM those JSON files when looking for blog post relations via category \ No newline at end of file diff --git a/json/site_content/category/_category.json b/json/site_content/category/_category.json new file mode 100644 index 0000000..3ccb49b --- /dev/null +++ b/json/site_content/category/_category.json @@ -0,0 +1,9 @@ +{ + "data": { + "category": { + "title": "Category Title", + "slug": "category-title", + "id": "1234567" + } + } +} \ No newline at end of file diff --git a/json/site_content/category/developers.json b/json/site_content/category/developers.json new file mode 100644 index 0000000..d4355f5 --- /dev/null +++ b/json/site_content/category/developers.json @@ -0,0 +1,9 @@ +{ + "data": { + "category": { + "id": "6311820", + "slug": "developers", + "title": "Developers" + } + } +} \ No newline at end of file diff --git a/json/site_content/category/insights.json b/json/site_content/category/insights.json new file mode 100644 index 0000000..8936025 --- /dev/null +++ b/json/site_content/category/insights.json @@ -0,0 +1,9 @@ +{ + "data": { + "category": { + "id": "6311819", + "slug": "insights", + "title": "Insights" + } + } +} \ No newline at end of file diff --git a/json/site_content/category/news.json b/json/site_content/category/news.json new file mode 100644 index 0000000..48c8cb8 --- /dev/null +++ b/json/site_content/category/news.json @@ -0,0 +1,9 @@ +{ + "data": { + "category": { + "id": "2965426", + "slug": "news", + "title": "News" + } + } +} \ No newline at end of file diff --git a/json/site_content/category/partners.json b/json/site_content/category/partners.json new file mode 100644 index 0000000..fc386d0 --- /dev/null +++ b/json/site_content/category/partners.json @@ -0,0 +1,9 @@ +{ + "data": { + "category": { + "id": "3545001", + "slug": "partners", + "title": "Partners" + } + } +} \ No newline at end of file diff --git a/json/site_content/category/product.json b/json/site_content/category/product.json new file mode 100644 index 0000000..6cef240 --- /dev/null +++ b/json/site_content/category/product.json @@ -0,0 +1,9 @@ +{ + "data": { + "category": { + "id": "3545003", + "slug": "product", + "title": "Product" + } + } +} \ No newline at end of file diff --git a/json/site_content/communityPage.json b/json/site_content/communityPage.json new file mode 100644 index 0000000..5e8f8b5 --- /dev/null +++ b/json/site_content/communityPage.json @@ -0,0 +1,31 @@ +{ + "data": { + "communityPage": { + "heroButton01": "JOIN US", + "heroButton02": "GET STARTED", + "heroDescription": "Join us to accelerate Web3 development, interoperability, and adoption.", + "heroHeading": "Laconic Community", + "heroB01Link": "https://discord.com/invite/ukhbBemyxY", + "heroB02Link": "https://github.com/cerc-io/stack-orchestrator", + "eventsHeading": "Events", + "eventsDescription": "We’d love to meet you in person. Find us at an event near you:", + "socialsHeading": "Connect with Us", + "socialsHeadingAlt": "Join Our Community", + "socialsLine": "GET IN TOUCH", + "socialsImage": { + "url": "/images/site_content/communityPage/socialsImage.png" + }, + "socialsImageLight": { + "url": "/images/site_content/communityPage/socialsImageLight.png" + }, + "socialsTwitter": "https://twitter.com/laconicnetwork", + "socialsDiscord": "https://discord.com/invite/ukhbBemyxY", + "socialsYoutube": "https://www.youtube.com/@Laconic-Network", + "socialsReddit": "https://www.reddit.com/r/LaconicNetwork/", + "socialsTelegram": "https://t.me/laconicnetwork", + "socialsLinkedin": "https://www.linkedin.com/company/laconic-network/", + "socialsFacebook": "https://www.facebook.com/laconicnetwork", + "socialsInstagram": "https://instagram.com/laconicnetwork" + } + } +} \ No newline at end of file diff --git a/json/site_content/contactPage.json b/json/site_content/contactPage.json new file mode 100644 index 0000000..e6e9db5 --- /dev/null +++ b/json/site_content/contactPage.json @@ -0,0 +1,41 @@ +{ + "data": { + "contactPage": { + "heroHeading": "Get in Touch", + "heroLine": "For general or technical inquiries", + "heroLink": "https://discord.com/invite/ukhbBemyxY", + "heroMobileLinkLabel": "CONTACT US", + "formHeading": "Contact Form", + "formWarning": "ALL FIELDS ARE REQUIRED", + "formLabelEmail": "Email", + "formPlaceholderEmail": "email@email.com*", + "formLabelMsg": "Message", + "formPlaceholderMsg": "Write a message or a comment*", + "formLabelPartner": "Type of inquiry", + "formPlaceholderPartner": "Inquiry*", + "formSelectOptions": [ + { + "value": "Partnership", + "label": "Partnership" + }, + { + "value": "Validator", + "label": "Validator/Member" + }, + { + "value": "Investor", + "label": "Investor" + }, + { + "value": "Press", + "label": "Press" + }, + { + "value": "Marketing", + "label": "Marketing" + } + ], + "formLabelButton": "SUBMIT MESSAGE" + } + } +} \ No newline at end of file diff --git a/json/site_content/event/20220921-mainnet.json b/json/site_content/event/20220921-mainnet.json new file mode 100644 index 0000000..a162153 --- /dev/null +++ b/json/site_content/event/20220921-mainnet.json @@ -0,0 +1,16 @@ +{ + "data": { + "event": { + "title": "Mainnet", + "eventDesc": "Mainnet is an immersive, agenda-setting summit held annually by Messari.", + "eventStartdate": "2022-09-21", + "eventEnddate": "2022-09-23", + "eventLocation": "New York, USA", + "id": "14069499", + "image": { + "url": "/images/site_content/event/20220921-mainnet.png" + }, + "link": "https://mainnet.events/" + } + } +} \ No newline at end of file diff --git a/json/site_content/event/20220927-cosmoverse.json b/json/site_content/event/20220927-cosmoverse.json new file mode 100644 index 0000000..f170aac --- /dev/null +++ b/json/site_content/event/20220927-cosmoverse.json @@ -0,0 +1,16 @@ +{ + "data": { + "event": { + "title": "Cosmoverse", + "eventDesc": "The legendary Cosmos Conference", + "eventStartdate": "2022-09-27", + "eventEnddate": "2022-09-28", + "eventLocation": "Medellin, Colombia", + "id": "14094472", + "image": { + "url": "/images/site_content/event/20220927-cosmoverse.png" + }, + "link": "https://www.cosmoverse.org/" + } + } +} \ No newline at end of file diff --git a/json/site_content/event/20221007-devcon.json b/json/site_content/event/20221007-devcon.json new file mode 100644 index 0000000..9c495a2 --- /dev/null +++ b/json/site_content/event/20221007-devcon.json @@ -0,0 +1,16 @@ +{ + "data": { + "event": { + "title": "Devcon", + "eventDesc": "Devcon is an intensive introduction for new Ethereum explorers, a global family reunion for those already a part of our ecosystem, and a source of energy and creativity for all.", + "eventStartdate": "2022-10-07", + "eventEnddate": "2022-10-14", + "eventLocation": " Bogota, Colombia", + "id": "14069491", + "image": { + "url": "/images/site_content/event/20221007-devcon.png" + }, + "link": "https://devcon.org/en/" + } + } +} \ No newline at end of file diff --git a/json/site_content/event/20221103-eth-sf.json b/json/site_content/event/20221103-eth-sf.json new file mode 100644 index 0000000..97f0ca5 --- /dev/null +++ b/json/site_content/event/20221103-eth-sf.json @@ -0,0 +1,16 @@ +{ + "data": { + "event": { + "title": "ETH SF", + "eventDesc": "ETHSanFrancisco 2022 will be the year’s premier Ethereum event on the west coast, so don’t miss your chance to see your SF friends' apartments one last time before they move to Miami.", + "eventStartdate": "2022-11-03", + "eventEnddate": "2022-11-05", + "eventLocation": "San Francisco, USA", + "id": "14094487", + "image": { + "url": "/images/site_content/event/20221103-eth-sf.png" + }, + "link": "https://sf.ethglobal.com/" + } + } +} \ No newline at end of file diff --git a/json/site_content/event/_event.json b/json/site_content/event/_event.json new file mode 100644 index 0000000..b2fe3c8 --- /dev/null +++ b/json/site_content/event/_event.json @@ -0,0 +1,16 @@ +{ + "data": { + "event": { + "title": "Event Title", + "eventDesc": "Event Description", + "eventStartdate": "2023-09-27", + "eventEnddate": "2023-09-28", + "eventLocation": "Event, Location", + "id": "12345678", + "image": { + "url": "/images/site_content/event/_default.jpg" + }, + "link": "https://www.google.com/" + } + } +} \ No newline at end of file diff --git a/json/site_content/footer.json b/json/site_content/footer.json new file mode 100644 index 0000000..a5ef3cc --- /dev/null +++ b/json/site_content/footer.json @@ -0,0 +1,123 @@ +{ + "data": { + "footer": { + "aboutLinks": [ + { + "href": "/about", + "title": "About" + }, + { + "href": "/about", + "title": "Vision" + }, + { + "href": "/about#team", + "title": "Team" + }, + { + "href": "/partners", + "title": "Partners" + }, + { + "href": "/careers", + "title": "Careers" + }, + { + "href": "/contact", + "title": "Contact" + }, + { + "href": "/assets/laconic-logo-assets.zip", + "title": "Brand" + } + ], + "communityLinks": [ + { + "href": "/community", + "title": "Community" + }, + { + "href": "/community#events", + "title": "Events" + } + ], + "connectLinks": [ + { + "href": "https://twitter.com/laconicnetwork", + "title": "Twitter" + }, + { + "href": "https://discord.com/invite/ukhbBemyxY", + "title": "Discord" + }, + { + "href": "https://www.youtube.com/@Laconic-Network", + "title": "Youtube" + }, + { + "href": "https://www.reddit.com/r/LaconicNetwork/", + "title": "Reddit" + }, + { + "href": "https://t.me/laconicnetwork", + "title": "Telegram" + }, + { + "href": "https://www.linkedin.com/company/laconic-network/", + "title": "Linkedin" + }, + { + "href": "https://www.facebook.com/laconicnetwork", + "title": "Facebook" + }, + { + "href": "https://instagram.com/laconicnetwork", + "title": "Instagram" + } + ], + "developerLinks": [ + { + "title": "Developers" + }, + { + "href": "https://github.com/LaconicNetwork", + "title": "Github" + }, + { + "href": "/about/#roadmap", + "title": "Roadmap" + }, + { + "href": "https://discord.com/invite/ukhbBemyxY", + "title": "Chat" + } + ], + "productsLinks": [ + { + "title": "Products", + "href": "/products" + }, + { + "href": "/products/#laconicwatchers", + "title": "Watchers (SDK)" + }, + { + "href": "/products/#laconicstack", + "title": "Laconic Stack" + }, + { + "href": "/products/#laconicnetwork", + "title": "Laconic Network" + }, + { + "href": "/products/#laconicapp", + "title": "Laconic App" + }, + { + "href": "/products/#laconictoken", + "title": "(LNT) Token" + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/header.json b/json/site_content/header.json new file mode 100644 index 0000000..9c04e08 --- /dev/null +++ b/json/site_content/header.json @@ -0,0 +1,119 @@ +{ + "data": { + "header": { + "navMenu": [ + { + "href": null, + "title": "Developers", + "content": [ + { + "href": "https://github.com/LaconicNetwork", + "title": "GitHub" + }, + { + "href": "/about#roadmap", + "title": "Roadmap" + }, + { + "href": "https://discord.com/invite/ukhbBemyxY", + "title": "Chat" + } + ] + }, + { + "href": "/products", + "title": "Products", + "content": [ + { + "href": "/products#laconicwatchers", + "title": "Watchers (SDK)" + }, + { + "href": "/products#laconicstack", + "title": "Stack" + }, + { + "href": "/products#laconicnetwork", + "title": "Network" + }, + { + "href": "/products#laconicapp", + "title": "App" + }, + { + "href": "/products#laconictoken", + "title": "Network Token (LNT)" + } + ] + }, + { + "href": "/partners", + "title": "Partners", + "content": [ + { + "href": "/partners#validators", + "title": "Member Validators" + }, + { + "href": "/partners#providers", + "title": "Service Providers" + }, + { + "href": "/partners#developers", + "title": "Developers" + }, + { + "href": "/partners#investors", + "title": "Investors" + }, + { + "href": "/partners#testnet", + "title": "Incentivized Testnet" + } + ] + }, + { + "href": "/about", + "title": "About", + "content": [ + { + "href": "/about", + "title": "Vision" + }, + { + "href": "/about#team", + "title": "Team" + }, + { + "href": "/careers", + "title": "Careers" + }, + { + "href": "/contact", + "title": "Contact" + }, + { + "href": "/assets/laconic-logo-assets.zip", + "title": "Brand" + } + ] + }, + { + "href": "/community", + "title": "Community", + "content": [ + { + "href": "/community#events", + "title": "Events" + } + ] + }, + { + "href": "/blog", + "title": "Blog", + "content": [] + } + ] + } + } +} \ No newline at end of file diff --git a/json/site_content/homePage.json b/json/site_content/homePage.json new file mode 100644 index 0000000..ab68ba1 --- /dev/null +++ b/json/site_content/homePage.json @@ -0,0 +1,34 @@ +{ + "data": { + "homePage": { + "heroHeading": "Build a
Fast, Frictionless,
& Verifiable
Web3", + "heroButton01": "JOIN US", + "heroB01Link": "https://discord.com/invite/ukhbBemyxY", + "heroButton02": "LEARN MORE", + "heroB02Link": "https://laconic.com/blog", + "heroSlogan": "

From months to minutes.
Laconic’s multi-chain verifiable data marketplace accelerates
development, interoperability, and adoption.

", + "benefitsDesc": "

Developed over five years by core Ethereum, IPLD / IPFS, and Cosmos SDK contributors, Laconic is a next generation data availability and verifiability layer with cryptographic proofs.

Combined with the world's first peer-to-peer data marketplace, Laconic makes internet-scale Web3 applications and light clients possible.

", + "benefitsHeading": "Why Laconic?", + "benefitsS01Desc": "Build blockchain applications faster, more efficiently, and at lower cost.", + "benefitsS01Heading": "For Developers", + "benefitsS01Item01": "Only solution with cryptographic proofs", + "benefitsS01Item02": "Composable, supports broad uses", + "benefitsS01Item03": "Quick automated setup", + "benefitsS01Item04": "Broad cross-chain compatibility", + "benefitsS01Item05": "Flexibility for all hash-linked data", + "benefitsS01Item06": "Lower development and data costs", + "benefitsS02Desc": "Experience unprecedented control, privacy, and usability.", + "benefitsS02Heading": "For App Users", + "benefitsS02Item01": "Greater data access and control", + "benefitsS02Item02": "In-browser local cache", + "benefitsS02Item03": "Optimal control of privacy levels", + "benefitsS02Item04": "Self-verify data accuracy", + "benefitsS02Item05": "Higher quality of service and uptime", + "benefitsS02Item06": "LNT rewards for network participation", + "benefitsButton": "Join Us", + "benefitsButtonLink": "https://discord.com/invite/ukhbBemyxY", + "othersHeading": "What Others Say", + "latestHeading": "Latest News" + } + } +} \ No newline at end of file diff --git a/json/site_content/partnersPage.json b/json/site_content/partnersPage.json new file mode 100644 index 0000000..0e7e5d5 --- /dev/null +++ b/json/site_content/partnersPage.json @@ -0,0 +1,91 @@ +{ + "data": { + "partnersPage": { + "heroHeading": "Join Us to Build Web3", + "heroParagraph01": "Laconic is ushering in a new era of internet-scale Web3 applications. We’re solving the hardest problems in Web3 by expanding access to verifiable data, and building a global incentivized peer-to-peer data marketplace.", + "heroParagraph02": "Interested in participating in the network? Individuals and organizations around the world with different levels of technical expertise can get involved:", + "oportunitiesDesc01": "Our Tendermint proof-of-stake (PoS) blockchain matches data consumers with service providers, and registers all of the available Laconic Watchers. In addition, the Laconic Network governance is all executed on-chain. Help us maintain the security of the blockchain and the Network by becoming a Laconic Member Validator.\n

\n• Participate in the Incentivized Testnet
\n• Run a Laconic Full-Index Node
\n• Help index blockchain data
\n• Host Watchers as a Service Provider", + "oportunitiesDesc02": "True internet-scale Web3 applications demand a robust and scalable data layer. Laconic Service Providers operate this layer by hosting Laconic Watchers that serve blockchain data to DApps. Earn by running the programs that provide DApp developers with their API endpoints.\n

\n• Operate data-serving APIs
\n• Receive payments from data consumers
\n• Build the global cache of blockchain data", + "oportunitiesDesc03": "No longer will Web3 developers struggle with data queries and transformations when building ambitious DApps. The power of Laconic Watchers to specify, transform, and cache blockchain data, and to create custom APIs tailored to individual DApps, will transform Web3 development.\n

\n• “Watch” any aspect of blockchain data
\n• Transform that data programmatically
\n• Build and expand on prior work - Watchers are composable
\n• Verify your data cryptographically", + "oportunitiesDesc04": "Help us solve the fundamental problems of blockchain adoption and scalability by creating a novel network of indexers and data providers. Whether your interest lies in joining the network as a Member, or helping us build a new generation of Web3 products on the Laconic Network, there is an opportunity for you.

\n\n• Become a Laconic Member through auctions
\n• Pair with operators to run the Laconic Stack
\n• Invest in the next generation of data-enabled Web3 apps", + "oportunitiesDesc05": "We are building the data scalability layer of all blockchains. This project requires professional operators who can manage hardware at scale. By participating in Laconic's Incentivized Testnet, you'll find out if you have what it takes to successfully run the Laconic Stack and meet our reliability and service quality standards. Completing the Incentivized Testnet is a prerequisite for bidding to become a Laconic Member.\n

\n• Run the Laconic Stack
\n• Complete challenges and improve the quality of the network
\n• Earn rewards
\n• Participate in Member auctions", + "oportunitiesHeading01": "Member Validators", + "oportunitiesHeading02": "Service Providers", + "oportunitiesHeading03": "Developers", + "oportunitiesHeading04": "Investors", + "oportunitiesHeading05": "Incentivized Testnet", + "oportunitiesImage01": { + "url": "/images/site_content/partnersPage/oportunitiesImage01.png" + }, + "oportunitiesImage01Light": { + "url": "/images/site_content/partnersPage/oportunitiesImage01Light.png" + }, + "oportunitiesImage02": { + "url": "/images/site_content/partnersPage/oportunitiesImage02.png" + }, + "oportunitiesImage02Light": { + "url": "/images/site_content/partnersPage/oportunitiesImage02Light.png" + }, + "oportunitiesImage03": { + "url": "/images/site_content/partnersPage/oportunitiesImage03.png" + }, + "oportunitiesImage03Light": { + "url": "/images/site_content/partnersPage/oportunitiesImage03Light.png" + }, + "oportunitiesImage04": { + "url": "/images/site_content/partnersPage/oportunitiesImage04.png" + }, + "oportunitiesImage04Light": { + "url": "/images/site_content/partnersPage/oportunitiesImage04Light.png" + }, + "oportunitiesImage05": { + "url": "/images/site_content/partnersPage/oportunitiesImage05.png" + }, + "oportunitiesImage05Light": { + "url": "/images/site_content/partnersPage/oportunitiesImage05Light.png" + }, + "oportunitiesItem01Links": null, + "oportunitiesItem02Links": null, + "oportunitiesItem03Links": null, + "oportunitiesItem04Links": null, + "oportunitiesItem05Links": null, + "contactButtonLabel": "GET IN TOUCH", + "contactCompanyLabel": "Company", + "contactCompanyPlaceholder": "Your company name*", + "contactDescription": "We have opportunities for people across different levels of technical expertise.", + "contactEmailLabel": "Email", + "contactEmailPlaceholder": "email@email.com*", + "contactFormHeading": "Share a bit about yourself, and join for early access.", + "contactFormWarning": "All fields are required", + "contactHeading": "Are you interested in participating in Laconic's Incentivized Testnet?", + "contactInquiryLabel": "Inquiry", + "contactInquiryOptions": [ + { + "value": "Validator", + "label": "Validator/Member" + }, + { + "value": "Investor", + "label": "Investor" + }, + { + "value": "Developer", + "label": "Developer" + }, + { + "value": "Customer", + "label": "Prospective Customer" + } + ], + "contactInquiryPlaceholder": "Investor*", + "contactLegalLabel": "What legal jurisdiction is your entity based in?", + "contactLegalPlaceholder": "Your jurisdiction*", + "contactMsgLabel": "Message", + "contactMsgPlaceholder": "Write a message or a comment*", + "contactNameLabel": "Name", + "contactNamePlaceholder": "Your name*", + "contactRoleLabel": "Role", + "contactRolePlaceholder": "Your role*" + } + } +} \ No newline at end of file diff --git a/json/site_content/position/__readme.txt b/json/site_content/position/__readme.txt new file mode 100644 index 0000000..f1e96ff --- /dev/null +++ b/json/site_content/position/__readme.txt @@ -0,0 +1,5 @@ +========NEXT_PUBLIC_DATOCMS_BYPASS /json/site_content/position/_readme.txt +-The JSON stub files in this directory were added for completion when migrating away from datocms +-There were currently no open positions on the careers page at the time of migrating +-However, they will populate on the careers page if valid JSON files are made per the template +-It should be noted, per the orig design of the careers page, the positions will only list if the team node includes "Cerc" or "Xylm" or "Deep Stack" \ No newline at end of file diff --git a/json/site_content/position/_position.json b/json/site_content/position/_position.json new file mode 100644 index 0000000..0fa7a0c --- /dev/null +++ b/json/site_content/position/_position.json @@ -0,0 +1,10 @@ +{ + "data": { + "position": { + "id": "12345678", + "positionName": "Position Name", + "positionTeam": "Cerc / Xylm / Deep Stack", + "positionLink": "https://laconic.com" + } + } +} \ No newline at end of file diff --git a/json/site_content/position/_testing/another-test-position.json b/json/site_content/position/_testing/another-test-position.json new file mode 100644 index 0000000..f2446d5 --- /dev/null +++ b/json/site_content/position/_testing/another-test-position.json @@ -0,0 +1,10 @@ +{ + "data": { + "position": { + "id": "12345678", + "positionName": "Another Position", + "positionTeam": "Cerc", + "positionLink": "https://laconic.com" + } + } +} \ No newline at end of file diff --git a/json/site_content/position/_testing/test-position.json b/json/site_content/position/_testing/test-position.json new file mode 100644 index 0000000..d8bbba6 --- /dev/null +++ b/json/site_content/position/_testing/test-position.json @@ -0,0 +1,10 @@ +{ + "data": { + "position": { + "id": "12345678", + "positionName": "Test Position", + "positionTeam": "Cerc", + "positionLink": "https://laconic.com" + } + } +} \ No newline at end of file diff --git a/json/site_content/pressPage.json b/json/site_content/pressPage.json new file mode 100644 index 0000000..fcc6e7e --- /dev/null +++ b/json/site_content/pressPage.json @@ -0,0 +1,35 @@ +{ + "data": { + "pressPage": { + "heroButtonLabel": "Download our press kit", + "heroButtonLink": "", + "heroHeading": "Newsroom", + "heroLine": "Contact press at xylm.io for press inquiries.", + "mediaHeading": "Media", + "mediaHeadingNumber": "04", + "mediaVideo01Label": "Laconic launching Ethereum rollups built with Cosmos tech", + "mediaVideo01Link": "viE0BZGx_DU", + "mediaVideo01Thumb": { + "url": "/images/site_content/pressPage/mediaVideo01Thumb.png" + }, + "mediaVideo02Label": "Hashing It Out Personals: Rick Dudley", + "mediaVideo02Link": "CZT99WsdSpE", + "mediaVideo02Thumb": { + "url": "/images/site_content/pressPage/mediaVideo02Thumb.png" + }, + "mediaVideo03Label": "hide this", + "mediaVideo03Link": "CZT99WsdSpE", + "mediaVideo03Thumb": { + "url": "/images/site_content/pressPage/mediaVideo03Thumb.png" + }, + "mediaVideo04Label": "hide this", + "mediaVideo04Link": "CZT99WsdSpE", + "mediaVideo04Thumb": { + "url": "/images/site_content/pressPage/mediaVideo04Thumb.png" + }, + "pressHeading": "Press Releases", + "pressHeadingNumber": "", + "featuredHeading": "Featured Stories" + } + } +} \ No newline at end of file diff --git a/json/site_content/pressRelease/__readme.txt b/json/site_content/pressRelease/__readme.txt new file mode 100644 index 0000000..948458e --- /dev/null +++ b/json/site_content/pressRelease/__readme.txt @@ -0,0 +1,4 @@ +========NEXT_PUBLIC_DATOCMS_BYPASS /json/site_content/pressRelease/_readme.txt +-The JSON stub files in this directory were added for completion when migrating away from datocms +-To my knowledge press releases are not called on the site in any location +-It looks like the queries used to list them were removed or never added to the press/newsletter page \ No newline at end of file diff --git a/json/site_content/pressRelease/_pressRelease.json b/json/site_content/pressRelease/_pressRelease.json new file mode 100644 index 0000000..37250bb --- /dev/null +++ b/json/site_content/pressRelease/_pressRelease.json @@ -0,0 +1,13 @@ +{ + "data": { + "pressRelease": { + "id": "12345678", + "title": "Press Release Title", + "date": "2023-04-19", + "link": "https://laconic.com", + "image": { + "url": "/images/site_content/pressRelease/_default.png" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/pressRelease/_testing/20230418-test.json b/json/site_content/pressRelease/_testing/20230418-test.json new file mode 100644 index 0000000..775f6f2 --- /dev/null +++ b/json/site_content/pressRelease/_testing/20230418-test.json @@ -0,0 +1,13 @@ +{ + "data": { + "pressRelease": { + "id": "12345678", + "title": "Test", + "date": "2023-04-18", + "link": "https://laconic.com", + "image": { + "url": "/images/site_content/pressRelease/_default.png" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/pressRelease/_testing/20230419-another-test.json b/json/site_content/pressRelease/_testing/20230419-another-test.json new file mode 100644 index 0000000..f6cafce --- /dev/null +++ b/json/site_content/pressRelease/_testing/20230419-another-test.json @@ -0,0 +1,13 @@ +{ + "data": { + "pressRelease": { + "id": "12345679", + "title": "Another Test", + "date": "2023-04-19", + "link": "https://laconic.com", + "image": { + "url": "/images/site_content/pressRelease/_default.png" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/privacyPage.json b/json/site_content/privacyPage.json new file mode 100644 index 0000000..484c0dd --- /dev/null +++ b/json/site_content/privacyPage.json @@ -0,0 +1,4348 @@ +{ + "data": { + "privacyPage": { + "privacyContent": { + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Privacy Policy" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We use Your Personal data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Interpretation and Definitions" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Interpretation" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Definitions" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the purposes of this Privacy Policy:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Account" + }, + { + "type": "span", + "value": " means a unique account created for You to access our Service or parts of our Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Business" + }, + { + "type": "span", + "value": ", for the purpose of the CCPA (California Consumer Privacy Act), refers to the Company as the legal entity that collects Consumers' personal information and determines the purposes and means of the processing of Consumers' personal information, or on behalf of which such information is collected and that alone, or jointly with others, determines the purposes and means of the processing of consumers' personal information, that does business in the State of California." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Company" + }, + { + "type": "span", + "value": " (referred to as either \"the Company\", \"We\", \"Us\" or \"Our\" in this Agreement) refers to Xylm Inc." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the purpose of the GDPR, the Company is the Data Controller." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Consumer" + }, + { + "type": "span", + "value": ", for the purpose of the CCPA (California Consumer Privacy Act), means a natural person who is a California resident. A resident, as defined in the law, includes (1) every individual who is in the USA for other than a temporary or transitory purpose, and (2) every individual who is domiciled in the USA who is outside the USA for a temporary or transitory purpose." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cookies" + }, + { + "type": "span", + "value": " are small files that are placed on Your computer, mobile device or any other device by a website, containing the details of Your browsing history on that website among its many uses." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Country" + }, + { + "type": "span", + "value": " refers to United States." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Data Controller" + }, + { + "type": "span", + "value": ", for the purposes of the GDPR (General Data Protection Regulation), refers to the Company as the legal person which alone or jointly with others determines the purposes and means of the processing of Personal Data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Device" + }, + { + "type": "span", + "value": " means any device that can access the Service such as a computer, a cellphone or a digital tablet." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Do Not Track" + }, + { + "type": "span", + "value": " (DNT) is a concept that has been promoted by US regulatory authorities, in particular the U.S. Federal Trade Commission (FTC), for the Internet industry to develop and implement a mechanism for allowing internet users to control the tracking of their online activities across websites." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Facebook Fan Page" + }, + { + "type": "span", + "value": " is a public profile named Laconic Network specifically created by the Company on the Facebook social network, accessible from " + }, + { + "url": "https://www.facebook.com/laconicnetwork", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://www.facebook.com/laconicnetwork" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Personal Data" + }, + { + "type": "span", + "value": " is any information that relates to an identified or identifiable individual." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the purposes of GDPR, Personal Data means any information relating to You such as a name, an identification number, location data, online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the purposes of the CCPA, Personal Data means any information that identifies, relates to, describes or is capable of being associated with, or could reasonably be linked, directly or indirectly, with You." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Sale" + }, + { + "type": "span", + "value": ", for the purpose of the CCPA (California Consumer Privacy Act), means selling, renting, releasing, disclosing, disseminating, making available, transferring, or otherwise communicating orally, in writing, or by electronic or other means, a Consumer's personal information to another business or a third party for monetary or other valuable consideration." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Service" + }, + { + "type": "span", + "value": " refers to the Website." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Service Provider" + }, + { + "type": "span", + "value": " means any natural or legal person who processes the data on behalf of the Company. It refers to third-party companies or individuals employed by the Company to facilitate the Service, to provide the Service on behalf of the Company, to perform services related to the Service or to assist the Company in analyzing how the Service is used. For the purpose of the GDPR, Service Providers are considered Data Processors." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Third-party Social Media Service" + }, + { + "type": "span", + "value": " refers to any website or any social network website through which a User can log in or create an account to use the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Usage Data" + }, + { + "type": "span", + "value": " refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Website" + }, + { + "type": "span", + "value": " refers to Laconic, accessible from " + }, + { + "url": "https://www.laconic.com/", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://www.laconic.com/" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "You" + }, + { + "type": "span", + "value": " means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Under GDPR (General Data Protection Regulation), You can be referred to as the Data Subject or as the User as you are the individual using the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collecting and Using Your Personal Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Types of Data Collected" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Personal Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "While using Our Service, We may ask You to provide Us with certain personally identifiable information that can be used to contact or identify You. Personally identifiable information may include, but is not limited to:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Email address" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "First name and last name" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Phone number" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Usage Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Usage Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Usage Data is collected automatically when using the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Usage Data may include information such as Your Device's Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that You visit, the time and date of Your visit, the time spent on those pages, unique device identifiers and other diagnostic data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "When You access the Service by or through a mobile device, We may collect certain information automatically, including, but not limited to, the type of mobile device You use, Your mobile device unique ID, the IP address of Your mobile device, Your mobile operating system, the type of mobile Internet browser You use, unique device identifiers and other diagnostic data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may also collect information that Your browser sends whenever You visit our Service or when You access the Service by or through a mobile device." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Information from Third-Party Social Media Services" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company allows You to create an account and log in to use the Service through the following Third-party Social Media Services:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Google" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Facebook" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Twitter" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "LinkedIn" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If You decide to register through or otherwise grant us access to a Third-Party Social Media Service, We may collect Personal data that is already associated with Your Third-Party Social Media Service's account, such as Your name, Your email address, Your activities or Your contact list associated with that account." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You may also have the option of sharing additional information with the Company through Your Third-Party Social Media Service's account. If You choose to provide such information and Personal Data, during registration or otherwise, You are giving the Company permission to use, share, and store it in a manner consistent with this Privacy Policy." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Tracking Technologies and Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We use Cookies and similar tracking technologies to track the activity on Our Service and store certain information. Tracking technologies used are beacons, tags, and scripts to collect and track information and to improve and analyze Our Service. The technologies We use may include:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cookies or Browser Cookies." + }, + { + "type": "span", + "value": " A cookie is a small file placed on Your Device. You can instruct Your browser to refuse all Cookies or to indicate when a Cookie is being sent. However, if You do not accept Cookies, You may not be able to use some parts of our Service. Unless you have adjusted Your browser setting so that it will refuse Cookies, our Service may use Cookies." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Flash Cookies." + }, + { + "type": "span", + "value": " Certain features of our Service may use local stored objects (or Flash Cookies) to collect and store information about Your preferences or Your activity on our Service. Flash Cookies are not managed by the same browser settings as those used for Browser Cookies. For more information on how You can delete Flash Cookies, please read \"Where can I change the settings for disabling, or deleting local shared objects?\" available at " + }, + { + "url": "https://helpx.adobe.com/flash-player/kb/disable-local-shared-objects-flash.html%23main_Where_can_I_change_the_settings_for_disabling__or_deleting_local_shared_objects_", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://helpx.adobe.com/flash-player/kb/disable-local-shared-objects-flash.html#main_Where_can_I_change_the_settings_for_disabling__or_deleting_local_shared_objects_" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Web Beacons." + }, + { + "type": "span", + "value": " Certain sections of our Service and our emails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit the Company, for example, to count users who have visited those pages or opened an email and for other related website statistics (for example, recording the popularity of a certain section and verifying system and server integrity)." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Cookies can be \"Persistent\" or \"Session\" Cookies. Persistent Cookies remain on Your personal computer or mobile device when You go offline, while Session Cookies are deleted as soon as You close Your web browser. Learn more about cookies on the " + }, + { + "url": "https://www.privacypolicies.com/blog/privacy-policy-template/%23Use_Of_Cookies_Log_Files_And_Tracking", + "type": "link", + "children": [ + { + "type": "span", + "value": "Privacy Policies website" + } + ] + }, + { + "type": "span", + "value": " article." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We use both Session and Persistent Cookies for the purposes set out below:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Necessary / Essential Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Type: Session Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Administered by: Us" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Purpose: These Cookies are essential to provide You with services available through the Website and to enable You to use some of its features. They help to authenticate users and prevent fraudulent use of user accounts. Without these Cookies, the services that You have asked for cannot be provided, and We only use these Cookies to provide You with those services." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Cookies Policy / Notice Acceptance Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Type: Persistent Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Administered by: Us" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Purpose: These Cookies identify if users have accepted the use of cookies on the Website." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Functionality Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Type: Persistent Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Administered by: Us" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Purpose: These Cookies allow us to remember choices You make when You use the Website, such as remembering your login details or language preference. The purpose of these Cookies is to provide You with a more personal experience and to avoid You having to re-enter your preferences every time You use the Website." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Tracking and Performance Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Type: Persistent Cookies" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Administered by: Third-Parties" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Purpose: These Cookies are used to track information about traffic to the Website and how users use the Website. The information gathered via these Cookies may directly or indirectly identify you as an individual visitor. This is because the information collected is typically linked to a pseudonymous identifier associated with the device you use to access the Website. We may also use these Cookies to test new pages, features or new functionality of the Website to see how our users react to them." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For more information about the cookies we use and your choices regarding cookies, please visit our Cookies Policy or the Cookies section of our Privacy Policy." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Use of Your Personal Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company may use Personal Data for the following purposes:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "To provide and maintain our Service" + }, + { + "type": "span", + "value": ", including to monitor the usage of our Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "To manage Your Account:" + }, + { + "type": "span", + "value": " to manage Your registration as a user of the Service. The Personal Data You provide can give You access to different functionalities of the Service that are available to You as a registered user." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "For the performance of a contract:" + }, + { + "type": "span", + "value": " the development, compliance and undertaking of the purchase contract for the products, items or services You have purchased or of any other contract with Us through the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "To contact You:" + }, + { + "type": "span", + "value": " To contact You by email, telephone calls, SMS, or other equivalent forms of electronic communication, such as a mobile application's push notifications regarding updates or informative communications related to the functionalities, products or contracted services, including the security updates, when necessary or reasonable for their implementation." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "To provide You" + }, + { + "type": "span", + "value": " with news, special offers and general information about other goods, services and events which we offer that are similar to those that you have already purchased or enquired about unless You have opted not to receive such information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "To manage Your requests:" + }, + { + "type": "span", + "value": " To attend and manage Your requests to Us." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "For business transfers:" + }, + { + "type": "span", + "value": " We may use Your information to evaluate or conduct a merger, divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all of Our assets, whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in which Personal Data held by Us about our Service users is among the assets transferred." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "For other purposes" + }, + { + "type": "span", + "value": ": We may use Your information for other purposes, such as data analysis, identifying usage trends, determining the effectiveness of our promotional campaigns and to evaluate and improve our Service, products, services, marketing and your experience." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may share Your personal information in the following situations:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "With Service Providers:" + }, + { + "type": "span", + "value": " We may share Your personal information with Service Providers to monitor and analyze the use of our Service, to contact You." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "For business transfers:" + }, + { + "type": "span", + "value": " We may share or transfer Your personal information in connection with, or during negotiations of, any merger, sale of Company assets, financing, or acquisition of all or a portion of Our business to another company." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "With Affiliates:" + }, + { + "type": "span", + "value": " We may share Your information with Our affiliates, in which case we will require those affiliates to honor this Privacy Policy. Affiliates include Our parent company and any other subsidiaries, joint venture partners or other companies that We control or that are under common control with Us." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "With business partners:" + }, + { + "type": "span", + "value": " We may share Your information with Our business partners to offer You certain products, services or promotions." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "With other users:" + }, + { + "type": "span", + "value": " when You share personal information or otherwise interact in the public areas with other users, such information may be viewed by all users and may be publicly distributed outside. If You interact with other users or register through a Third-Party Social Media Service, Your contacts on the Third-Party Social Media Service may see Your name, profile, pictures and description of Your activity. Similarly, other users will be able to view descriptions of Your activity, communicate with You and view Your profile." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "With Your consent" + }, + { + "type": "span", + "value": ": We may disclose Your personal information for any other purpose with Your consent." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Retention of Your Personal Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company will retain Your Personal Data only for as long as is necessary for the purposes set out in this Privacy Policy. We will retain and use Your Personal Data to the extent necessary to comply with our legal obligations (for example, if we are required to retain your data to comply with applicable laws), resolve disputes, and enforce our legal agreements and policies." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company will also retain Usage Data for internal analysis purposes. Usage Data is generally retained for a shorter period of time, except when this data is used to strengthen the security or to improve the functionality of Our Service, or We are legally obligated to retain this data for longer time periods." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Transfer of Your Personal Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your information, including Personal Data, is processed at the Company's operating offices and in any other places where the parties involved in the processing are located. It means that this information may be transferred to — and maintained on — computers located outside of Your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from Your jurisdiction." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your consent to this Privacy Policy followed by Your submission of such information represents Your agreement to that transfer." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company will take all steps reasonably necessary to ensure that Your data is treated securely and in accordance with this Privacy Policy and no transfer of Your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of Your data and other personal information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Disclosure of Your Personal Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Business Transactions" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If the Company is involved in a merger, acquisition or asset sale, Your Personal Data may be transferred. We will provide notice before Your Personal Data is transferred and becomes subject to a different Privacy Policy." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Law enforcement" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Under certain circumstances, the Company may be required to disclose Your Personal Data if required to do so by law or in response to valid requests by public authorities (e.g. a court or a government agency)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Other legal requirements" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company may disclose Your Personal Data in the good faith belief that such action is necessary to:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Comply with a legal obligation" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Protect and defend the rights or property of the Company" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Prevent or investigate possible wrongdoing in connection with the Service" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Protect the personal safety of Users of the Service or the public" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Protect against legal liability" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Security of Your Personal Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The security of Your Personal Data is important to Us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While We strive to use commercially acceptable means to protect Your Personal Data, We cannot guarantee its absolute security." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Detailed Information on the Processing of Your Personal Data" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Service Providers We use may have access to Your Personal Data. These third-party vendors collect, store, use, process and transfer information about Your activity on Our Service in accordance with their Privacy Policies." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Analytics" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may use third-party Service providers to monitor and analyze the use of our Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Google Analytics" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Google Analytics is a web analytics service offered by Google that tracks and reports website traffic. Google uses the data collected to track and monitor the use of our Service. This data is shared with other Google services. Google may use the collected data to contextualize and personalize the ads of its own advertising network." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You can opt-out of having made your activity on the Service available to Google Analytics by installing the Google Analytics opt-out browser add-on. The add-on prevents the Google Analytics JavaScript (ga.js, analytics.js and dc.js) from sharing information with Google Analytics about visits activity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For more information on the privacy practices of Google, please visit the Google Privacy & Terms web page: " + }, + { + "url": "https://policies.google.com/privacy", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://policies.google.com/privacy" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Email Marketing" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may use Your Personal Data to contact You with newsletters, marketing or promotional materials and other information that may be of interest to You. You may opt-out of receiving any, or all, of these communications from Us by following the unsubscribe link or instructions provided in any email We send or by contacting Us." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may use Email Marketing Service Providers to manage and send emails to You." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Hubspot" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Their Privacy Policy can be viewed at " + }, + { + "url": "https://legal.hubspot.com/privacy-policy", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://legal.hubspot.com/privacy-policy" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "GDPR Privacy" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Legal Basis for Processing Personal Data under GDPR" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may process Personal Data under the following conditions:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Consent:" + }, + { + "type": "span", + "value": " You have given Your consent for processing Personal Data for one or more specific purposes." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Performance of a contract:" + }, + { + "type": "span", + "value": " Provision of Personal Data is necessary for the performance of an agreement with You and/or for any pre-contractual obligations thereof." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Legal obligations:" + }, + { + "type": "span", + "value": " Processing Personal Data is necessary for compliance with a legal obligation to which the Company is subject." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Vital interests:" + }, + { + "type": "span", + "value": " Processing Personal Data is necessary in order to protect Your vital interests or of another natural person." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Public interests:" + }, + { + "type": "span", + "value": " Processing Personal Data is related to a task that is carried out in the public interest or in the exercise of official authority vested in the Company." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Legitimate interests:" + }, + { + "type": "span", + "value": " Processing Personal Data is necessary for the purposes of the legitimate interests pursued by the Company." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In any case, the Company will gladly help to clarify the specific legal basis that applies to the processing, and in particular whether the provision of Personal Data is a statutory or contractual requirement, or a requirement necessary to enter into a contract." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Your Rights under the GDPR" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company undertakes to respect the confidentiality of Your Personal Data and to guarantee You can exercise Your rights." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You have the right under this Privacy Policy, and by law if You are within the EU, to:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Request access to Your Personal Data." + }, + { + "type": "span", + "value": " The right to access, update or delete the information We have on You. Whenever made possible, you can access, update or request deletion of Your Personal Data directly within Your account settings section. If you are unable to perform these actions yourself, please contact Us to assist You. This also enables You to receive a copy of the Personal Data We hold about You." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Request correction of the Personal Data that We hold about You." + }, + { + "type": "span", + "value": " You have the right to have any incomplete or inaccurate information We hold about You corrected." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Object to processing of Your Personal Data." + }, + { + "type": "span", + "value": " This right exists where We are relying on a legitimate interest as the legal basis for Our processing and there is something about Your particular situation, which makes You want to object to our processing of Your Personal Data on this ground. You also have the right to object where We are processing Your Personal Data for direct marketing purposes." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Request erasure of Your Personal Data." + }, + { + "type": "span", + "value": " You have the right to ask Us to delete or remove Personal Data when there is no good reason for Us to continue processing it." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Request the transfer of Your Personal Data." + }, + { + "type": "span", + "value": " We will provide to You, or to a third-party You have chosen, Your Personal Data in a structured, commonly used, machine-readable format. Please note that this right only applies to automated information which You initially provided consent for Us to use or where We used the information to perform a contract with You." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Withdraw Your consent." + }, + { + "type": "span", + "value": " You have the right to withdraw Your consent on using your Personal Data. If You withdraw Your consent, We may not be able to provide You with access to certain specific functionalities of the Service." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Exercising of Your GDPR Data Protection Rights" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You may exercise Your rights of access, rectification, cancellation and opposition by contacting Us. Please note that we may ask You to verify Your identity before responding to such requests. If You make a request, We will try our best to respond to You as soon as possible." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You have the right to complain to a Data Protection Authority about Our collection and use of Your Personal Data. For more information, if You are in the European Economic Area (EEA), please contact Your local data protection authority in the EEA." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Facebook Fan Page" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Data Controller for the Facebook Fan Page" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company is the Data Controller of Your Personal Data collected while using the Service. As operator of the Facebook Fan Page " + }, + { + "url": "https://www.facebook.com/laconicnetwork", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://www.facebook.com/laconicnetwork" + } + ] + }, + { + "type": "span", + "value": ", the Company and the operator of the social network Facebook are Joint Controllers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company has entered into agreements with Facebook that define the terms for use of the Facebook Fan Page, among other things. These terms are mostly based on the Facebook Terms of Service: " + }, + { + "url": "https://www.facebook.com/terms.php", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://www.facebook.com/terms.php" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Visit the Facebook Privacy Policy " + }, + { + "url": "https://www.facebook.com/policy.php", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://www.facebook.com/policy.php" + } + ] + }, + { + "type": "span", + "value": " for more information about how Facebook manages Personal data or contact Facebook online, or by mail: Facebook, Inc. ATTN, Privacy Operations, 1601 Willow Road, Menlo Park, CA 94025, United States." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Facebook Insights" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We use the Facebook Insights function in connection with the operation of the Facebook Fan Page and on the basis of the GDPR, in order to obtain anonymized statistical data about Our users." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For this purpose, Facebook places a Cookie on the device of the user visiting Our Facebook Fan Page. Each Cookie contains a unique identifier code and remains active for a period of two years, except when it is deleted before the end of this period." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Facebook receives, records and processes the information stored in the Cookie, especially when the user visits the Facebook services, services that are provided by other members of the Facebook Fan Page and services by other companies that use Facebook services." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For more information on the privacy practices of Facebook, please visit Facebook Privacy Policy here: " + }, + { + "url": "https://www.facebook.com/privacy/explanation", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://www.facebook.com/privacy/explanation" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "CCPA Privacy" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "This privacy notice section for California residents supplements the information contained in Our Privacy Policy and it applies solely to all visitors, users, and others who reside in the State of California." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Categories of Personal Information Collected" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We collect information that identifies, relates to, describes, references, is capable of being associated with, or could reasonably be linked, directly or indirectly, with a particular Consumer or Device. The following is a list of categories of personal information which we may collect or may have been collected from California residents within the last twelve (12) months." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Please note that the categories and examples provided in the list below are those defined in the CCPA. This does not mean that all examples of that category of personal information were in fact collected by Us, but reflects our good faith belief to the best of our knowledge that some of that information from the applicable category may be and may have been collected. For example, certain categories of personal information would only be collected if You provided such personal information directly to Us." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category A: Identifiers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: A real name, alias, postal address, unique personal identifier, online identifier, Internet Protocol address, email address, account name, driver's license number, passport number, or other similar identifiers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: Yes." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category B: Personal information categories listed in the California Customer Records statute (Cal. Civ. Code § 1798.80(e))." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: A name, signature, Social Security number, physical characteristics or description, address, telephone number, passport number, driver's license or state identification card number, insurance policy number, education, employment, employment history, bank account number, credit card number, debit card number, or any other financial information, medical information, or health insurance information. Some personal information included in this category may overlap with other categories." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: Yes." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category C: Protected classification characteristics under California or federal law." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Age (40 years or older), race, color, ancestry, national origin, citizenship, religion or creed, marital status, medical condition, physical or mental disability, sex (including gender, gender identity, gender expression, pregnancy or childbirth and related medical conditions), sexual orientation, veteran or military status, genetic information (including familial genetic information)." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: No." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category D: Commercial information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Records and history of products or services purchased or considered." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: No." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category E: Biometric information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Genetic, physiological, behavioral, and biological characteristics, or activity patterns used to extract a template or other identifier or identifying information, such as, fingerprints, faceprints, and voiceprints, iris or retina scans, keystroke, gait, or other physical patterns, and sleep, health, or exercise data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: No." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category F: Internet or other similar network activity." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Interaction with our Service or advertisement." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: Yes." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category G: Geolocation data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Approximate physical location." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: No." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category H: Sensory data." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Audio, electronic, visual, thermal, olfactory, or similar information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: No." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category I: Professional or employment-related information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Current or past job history or performance evaluations." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: No." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category J: Non-public education information (per the Family Educational Rights and Privacy Act (20 U.S.C. Section 1232g, 34 C.F.R. Part 99))." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Education records directly related to a student maintained by an educational institution or party acting on its behalf, such as grades, transcripts, class lists, student schedules, student identification codes, student financial information, or student disciplinary records." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: No." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Category K: Inferences drawn from other personal information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Examples: Profile reflecting a person's preferences, characteristics, psychological trends, predispositions, behavior, attitudes, intelligence, abilities, and aptitudes." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Collected: No." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Under CCPA, personal information does not include:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Publicly available information from government records" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Deidentified or aggregated consumer information" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Information excluded from the CCPA's scope, such as: " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Health or medical information covered by the Health Insurance Portability and Accountability Act of 1996 (HIPAA) and the California Confidentiality of Medical Information Act (CMIA) or clinical trial data" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Personal Information covered by certain sector-specific privacy laws, including the Fair Credit Reporting Act (FRCA), the Gramm-Leach-Bliley Act (GLBA) or California Financial Information Privacy Act (FIPA), and the Driver's Privacy Protection Act of 1994" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Sources of Personal Information" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We obtain the categories of personal information listed above from the following categories of sources:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Directly from You" + }, + { + "type": "span", + "value": ". For example, from the forms You complete on our Service, preferences You express or provide through our Service." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Indirectly from You" + }, + { + "type": "span", + "value": ". For example, from observing Your activity on our Service." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Automatically from You" + }, + { + "type": "span", + "value": ". For example, through cookies We or our Service Providers set on Your Device as You navigate through our Service." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "From Service Providers" + }, + { + "type": "span", + "value": ". For example, third-party vendors to monitor and analyze the use of our Service, or other third-party vendors that We use to provide the Service to You." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Use of Personal Information for Business Purposes or Commercial Purposes" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may use or disclose personal information We collect for \"business purposes\" or \"commercial purposes\" (as defined under the CCPA), which may include the following examples:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To operate our Service and provide You with our Service." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To provide You with support and to respond to Your inquiries, including to investigate and address Your concerns and monitor and improve our Service." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To fulfill or meet the reason You provided the information. For example, if You share Your contact information to ask a question about our Service, We will use that personal information to respond to Your inquiry." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To respond to law enforcement requests and as required by applicable law, court order, or governmental regulations." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As described to You when collecting Your personal information or as otherwise set forth in the CCPA." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For internal administrative and auditing purposes." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To detect security incidents and protect against malicious, deceptive, fraudulent or illegal activity, including, when necessary, to prosecute those responsible for such activities." + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Please note that the examples provided above are illustrative and not intended to be exhaustive. For more details on how we use this information, please refer to the \"Use of Your Personal Data\" section." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If We decide to collect additional categories of personal information or use the personal information We collected for materially different, unrelated, or incompatible purposes We will update this Privacy Policy." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Disclosure of Personal Information for Business Purposes or Commercial Purposes" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may use or disclose and may have used or disclosed in the last twelve (12) months the following categories of personal information for business or commercial purposes:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Category A: Identifiers" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Category B: Personal information categories listed in the California Customer Records statute (Cal. Civ. Code § 1798.80(e))" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Category F: Internet or other similar network activity" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Please note that the categories listed above are those defined in the CCPA. This does not mean that all examples of that category of personal information were in fact disclosed, but reflects our good faith belief to the best of our knowledge that some of that information from the applicable category may be and may have been disclosed." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "When We disclose personal information for a business purpose or a commercial purpose, We enter a contract that describes the purpose and requires the recipient to both keep that personal information confidential and not use it for any purpose except performing the contract." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Sale of Personal Information" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "As defined in the CCPA, \"sell\" and \"sale\" mean selling, renting, releasing, disclosing, disseminating, making available, transferring, or otherwise communicating orally, in writing, or by electronic or other means, a consumer's personal information by the business to a third party for valuable consideration. This means that We may have received some kind of benefit in return for sharing personal information, but not necessarily a monetary benefit." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Please note that the categories listed below are those defined in the CCPA. This does not mean that all examples of that category of personal information were in fact sold, but reflects our good faith belief to the best of our knowledge that some of that information from the applicable category may be and may have been shared for value in return." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may sell and may have sold in the last twelve (12) months the following categories of personal information:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Category A: Identifiers" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Category B: Personal information categories listed in the California Customer Records statute (Cal. Civ. Code § 1798.80(e))" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Category F: Internet or other similar network activity" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Share of Personal Information" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may share Your personal information identified in the above categories with the following categories of third parties:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Service Providers" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our affiliates" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our business partners" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Third party vendors to whom You or Your agents authorize Us to disclose Your personal information in connection with products or services We provide to You" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Sale of Personal Information of Minors Under 16 Years of Age" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We do not knowingly collect personal information from minors under the age of 16 through our Service, although certain third party websites that we link to may do so. These third-party websites have their own terms of use and privacy policies and we encourage parents and legal guardians to monitor their children's Internet usage and instruct their children to never provide information on other websites without their permission." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We do not sell the personal information of consumers we actually know are less than 16 years of age, unless we receive affirmative authorization (the \"right to opt-in\") from either the Consumer who is between 13 and 16 years of age, or the parent or guardian of a consumer less than 13 years of age. Consumers who opt-in to the sale of personal information may opt-out of future sales at any time. To exercise the right to opt-out, You (or Your authorized representative) may submit a request to Us by contacting Us." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If You have reason to believe that a child under the age of 13 (or 16) has provided Us with personal information, please contact Us with sufficient detail to enable Us to delete that information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Your Rights under the CCPA" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The CCPA provides California residents with specific rights regarding their personal information. If You are a resident of California, You have the following rights:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The right to notice." + }, + { + "type": "span", + "value": " You have the right to be notified which categories of Personal Data are being collected and the purposes for which the Personal Data is being used." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The right to request." + }, + { + "type": "span", + "value": " Under CCPA, You have the right to request that We disclose information to You about Our collection, use, sale, disclosure for business purposes and share of personal information. Once We receive and confirm Your request, We will disclose to You: " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The categories of personal information We collected about You" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The categories of sources for the personal information We collected about You" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our business or commercial purpose for collecting or selling that personal information" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The categories of third parties with whom We share that personal information" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The specific pieces of personal information We collected about You" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If we sold Your personal information or disclosed Your personal information for a business purpose, We will disclose to You: " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The categories of personal information categories sold" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The categories of personal information categories disclosed" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The right to say no to the sale of Personal Data (opt-out)." + }, + { + "type": "span", + "value": " You have the right to direct Us to not sell Your personal information. To submit an opt-out request please contact Us." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The right to delete Personal Data." + }, + { + "type": "span", + "value": " You have the right to request the deletion of Your Personal Data, subject to certain exceptions. Once We receive and confirm Your request, We will delete (and direct Our Service Providers to delete) Your personal information from our records, unless an exception applies. We may deny Your deletion request if retaining the information is necessary for Us or Our Service Providers to: " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Complete the transaction for which We collected the personal information, provide a good or service that You requested, take actions reasonably anticipated within the context of our ongoing business relationship with You, or otherwise perform our contract with You." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Detect security incidents, protect against malicious, deceptive, fraudulent, or illegal activity, or prosecute those responsible for such activities." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Debug products to identify and repair errors that impair existing intended functionality." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Exercise free speech, ensure the right of another consumer to exercise their free speech rights, or exercise another right provided for by law." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Comply with the California Electronic Communications Privacy Act (Cal. Penal Code § 1546 et. seq.)." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Engage in public or peer-reviewed scientific, historical, or statistical research in the public interest that adheres to all other applicable ethics and privacy laws, when the information's deletion may likely render impossible or seriously impair the research's achievement, if You previously provided informed consent." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Enable solely internal uses that are reasonably aligned with consumer expectations based on Your relationship with Us." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Comply with a legal obligation." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Make other internal and lawful uses of that information that are compatible with the context in which You provided it." + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "The right not to be discriminated against." + }, + { + "type": "span", + "value": " You have the right not to be discriminated against for exercising any of Your consumer's rights, including by: " + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Denying goods or services to You" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Charging different prices or rates for goods or services, including the use of discounts or other benefits or imposing penalties" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Providing a different level or quality of goods or services to You" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Suggesting that You will receive a different price or rate for goods or services or a different level or quality of goods or services" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Exercising Your CCPA Data Protection Rights" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "In order to exercise any of Your rights under the CCPA, and if You are a California resident, You can contact Us:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "By email: info@xylm.io" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Only You, or a person registered with the California Secretary of State that You authorize to act on Your behalf, may make a verifiable request related to Your personal information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your request to Us must:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Provide sufficient information that allows Us to reasonably verify You are the person about whom We collected personal information or an authorized representative" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Describe Your request with sufficient detail that allows Us to properly understand, evaluate, and respond to it" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We cannot respond to Your request or provide You with the required information if we cannot:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Verify Your identity or authority to make the request" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "And confirm that the personal information relates to You" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We will disclose and deliver the required information free of charge within 45 days of receiving Your verifiable request. The time period to provide the required information may be extended once by an additional 45 days when reasonably necessary and with prior notice." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Any disclosures We provide will only cover the 12-month period preceding the verifiable request's receipt." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For data portability requests, We will select a format to provide Your personal information that is readily usable and should allow You to transmit the information from one entity to another entity without hindrance." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Do Not Sell My Personal Information" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You have the right to opt-out of the sale of Your personal information. Once We receive and confirm a verifiable consumer request from You, we will stop selling Your personal information. To exercise Your right to opt-out, please contact Us." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Service Providers we partner with (for example, our analytics or advertising partners) may use technology on the Service that sells personal information as defined by the CCPA law. If you wish to opt out of the use of Your personal information for interest-based advertising purposes and these potential sales as defined under CCPA law, you may do so by following the instructions below." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Please note that any opt out is specific to the browser You use. You may need to opt out on every browser that You use." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Website" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You can opt out of receiving ads that are personalized as served by our Service Providers by following our instructions presented on the Service:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The NAI's opt-out platform: " + }, + { + "url": "http://www.networkadvertising.org/choices/", + "type": "link", + "children": [ + { + "type": "span", + "value": "http://www.networkadvertising.org/choices/" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The EDAA's opt-out platform " + }, + { + "url": "http://www.youronlinechoices.com/", + "type": "link", + "children": [ + { + "type": "span", + "value": "http://www.youronlinechoices.com/" + } + ] + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The DAA's opt-out platform: " + }, + { + "url": "http://optout.aboutads.info/?c=2&lang=EN", + "type": "link", + "children": [ + { + "type": "span", + "value": "http://optout.aboutads.info/?c=2&lang=EN" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The opt out will place a cookie on Your computer that is unique to the browser You use to opt out. If you change browsers or delete the cookies saved by your browser, You will need to opt out again." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Mobile Devices" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your mobile device may give You the ability to opt out of the use of information about the apps You use in order to serve You ads that are targeted to Your interests:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\"Opt out of Interest-Based Ads\" or \"Opt out of Ads Personalization\" on Android devices" + } + ] + } + ] + }, + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\"Limit Ad Tracking\" on iOS devices" + } + ] + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You can also stop the collection of location information from Your mobile device by changing the preferences on Your mobile device." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "\"Do Not Track\" Policy as Required by California Online Privacy Protection Act (CalOPPA)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our Service does not respond to Do Not Track signals." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "However, some third party websites do keep track of Your browsing activities. If You are visiting such websites, You can set Your preferences in Your web browser to inform websites that You do not want to be tracked. You can enable or disable DNT by visiting the preferences or settings page of Your web browser." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Children's Privacy" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our Service does not address anyone under the age of 13. We do not knowingly collect personally identifiable information from anyone under the age of 13. If You are a parent or guardian and You are aware that Your child has provided Us with Personal Data, please contact Us. If We become aware that We have collected Personal Data from anyone under the age of 13 without verification of parental consent, We take steps to remove that information from Our servers." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If We need to rely on consent as a legal basis for processing Your information and Your country requires consent from a parent, We may require Your parent's consent before We collect and use that information." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Your California Privacy Rights (California's Shine the Light law)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Under California Civil Code Section 1798 (California's Shine the Light law), California residents with an established business relationship with us can request information once a year about sharing their Personal Data with third parties for the third parties' direct marketing purposes." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you'd like to request more information under the California Shine the Light law, and if You are a California resident, You can contact Us using the contact information provided below." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "California Privacy Rights for Minor Users (California Business and Professions Code Section 22581)" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "California Business and Professions Code Section 22581 allows California residents under the age of 18 who are registered users of online sites, services or applications to request and obtain removal of content or information they have publicly posted." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To request removal of such data, and if You are a California resident, You can contact Us using the contact information provided below, and include the email address associated with Your account." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Be aware that Your request does not guarantee complete or comprehensive removal of content or information posted online and that the law may not permit or require removal in certain circumstances." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Links to Other Websites" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our Service may contain links to other websites that are not operated by Us. If You click on a third party link, You will be directed to that third party's site. We strongly advise You to review the Privacy Policy of every site You visit." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Changes to this Privacy Policy" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may update Our Privacy Policy from time to time. We will notify You of any changes by posting the new Privacy Policy on this page." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We will let You know via email and/or a prominent notice on Our Service, prior to the change becoming effective and update the \"Last updated\" date at the top of this Privacy Policy." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Contact Us" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you have any questions about this Privacy Policy, You can contact us:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "By email: info@xylm.io" + } + ] + } + ] + } + ] + } + ] + } + }, + "blocks": [] + }, + "_updatedAt": "2022-08-15T21:56:38-07:00" + } + } +} \ No newline at end of file diff --git a/json/site_content/productsPage.json b/json/site_content/productsPage.json new file mode 100644 index 0000000..d45cded --- /dev/null +++ b/json/site_content/productsPage.json @@ -0,0 +1,122 @@ +{ + "data": { + "productsPage": { + "appDescription": "Your front-end to Laconic Network. Preview, monitor and manage your DApp’s Watcher usage. Participate in network governance. Then chart a path to deeper participation in the growth & stability of the network.

\n• In-browser IPFS payment channels
\n• Private self-custodied account data
\n• Access to member auctions & resources", + "appHeading": "Laconic App", + "appImg": { + "url": "/images/site_content/productsPage/appImg.png" + }, + "appImgLight": { + "url": "/images/site_content/productsPage/appImgLight.png" + }, + "appMobileImg": { + "url": "/images/site_content/productsPage/appMobileImg.jpg" + }, + "appMobileImgLight": { + "url": "/images/site_content/productsPage/appMobileImgLight.jpg" + }, + "heroDescription": "Developed over five years with cutting-edge data-caching engineering, Laconic's technology stack and marketplace powers internet-scale Web3 applications.", + "heroHeading": "Products", + "heroItem01": "Laconic Watchers (SDK)", + "heroItem02": "Laconic Stack", + "heroItem03": "Laconic Network", + "heroItem04": "Laconic App", + "heroItem05": "Laconic Network Token (LNT)", + "heroS01Desc": "Laconic enables simple and efficient DApp development, and the most flexible views of the Ethereum state.", + "heroS01Heading": "DeFi", + "heroS02Desc": "Laconic exposes cross-chain history and metadata associated with an NFT, enabling provably accurate data for DApps and Marketplaces.", + "heroS02Heading": "NFTs", + "networkDesc": "Laconic Network is an open, interoperable, verifiable data marketplace and ecosystem of service providers, DApp operators, and data consumers providing low-cost, decentralized, and disintermediated horizontal scaling solutions for projects leveraging Laconic Stack.", + "networkHeading": "Laconic Network", + "networkImg": { + "url": "/images/site_content/productsPage/networkImg.jpg" + }, + "networkImgLight": { + "url": "/images/site_content/productsPage/networkImgLight.jpg" + }, + "networkListItem01": "Seamlessly connects DApps and bridges with data service providers", + "networkListItem02": "Leverages state-channel-based payment infrastructure", + "networkListItem03": "Provides highest quality services at the lowest prices via marketplace model", + "networkMobileImg": { + "url": "/images/site_content/productsPage/networkMobileImg.jpg" + }, + "networkMobileImgLight": { + "url": "/images/site_content/productsPage/networkMobileImgLight.jpg" + }, + "stackDescription": "Laconic Stack is a set of essential and verifiable technologies and services that significantly simplify DApp development and make DApps accessible to a wider audience of developers.", + "stackHeading": "Laconic Stack", + "stackImage": { + "url": "/images/site_content/productsPage/stackImage.png" + }, + "stackImageLight": { + "url": "/images/site_content/productsPage/stackImageLight.png" + }, + "stackListData": { + "stackData": [ + { + "title": "Core", + "content": "Ethereum node, ipld-eth-server, and relational database" + }, + { + "title": "Watcher Service", + "content": "Allows services or browsers to subscribe to data" + }, + { + "title": "Tracing Service", + "content": "Generates and caches traces" + }, + { + "title": "Eth State Diff Service", + "content": "Serves state diffs from an offline Geth database" + }, + { + "title": "Pending Transaction Service", + "content": "Provides real-time transaction pool data" + } + ] + }, + "stackSvgImg": { + "url": "/images/site_content/productsPage/stackSvgImg.svg" + }, + "stackSvgImgLight": { + "url": "/images/site_content/productsPage/stackSvgImgLight.svg" + }, + "tokenDesc": "With a fixed total supply, Laconic Network Token (LNT) serves multiple functions in the Network design, most saliently by securing incentive alignment across network stakeholders. LNT also allows for seamless payments across services, regardless of blockchain or DApp.", + "tokenHeading": "Laconic Network Token", + "tokenImg": { + "url": "/images/site_content/productsPage/tokenImg.jpg" + }, + "tokenImgLight": { + "url": "/images/site_content/productsPage/tokenImgLight.jpg" + }, + "tokenItem01": "Allow seamless payments for services", + "tokenItem02": "Provide service discounts", + "tokenItem03": "Award community development grants", + "tokenMobileImg": { + "url": "/images/site_content/productsPage/tokenMobileImg.jpg" + }, + "tokenMobileImgLight": { + "url": "/images/site_content/productsPage/tokenMobileImgLight.jpg" + }, + "watchersHeading": "Laconic
Watchers
(SDK)", + "watchersImage": { + "url": "/images/site_content/productsPage/watchersImage.jpg" + }, + "watchersImageLight": { + "url": "/images/site_content/productsPage/watchersImageLight.jpg" + }, + "watchersItem01": "Query the data a DApp needs", + "watchersItem02": "Make that data consumable to the DApp", + "watchersItem03": "Cache the data for fast and inexpensive access", + "watchersListHead": "A Watcher serves three fundamental purposes:", + "watchersMobileImage": { + "url": "/images/site_content/productsPage/watchersMobileImage.jpg" + }, + "watchersMobileImageLight": { + "url": "/images/site_content/productsPage/watchersMobileImageLight.jpg" + }, + "watchersP01": "Leverage the Laconic SDK to create Watchers. Watchers are customized queries of blockchain data against specific smart contracts. Watchers enable fast and frictionless DApp development. Through Watchers, a DApp’s data can be fully verifiable with hundreds of megabytes instead of tens of terabytes. Use Watchers to dramatically increase speed and efficiency while lowering costs.", + "watchersP02": "" + } + } +} \ No newline at end of file diff --git a/json/site_content/team/_team.json b/json/site_content/team/_team.json new file mode 100644 index 0000000..53bb080 --- /dev/null +++ b/json/site_content/team/_team.json @@ -0,0 +1,19 @@ +{ + "data": { + "team": { + "memberGithub": "https://github.com/github-handle", + "memberLinkedin": "https://www.linkedin.com/in/linked-in-url/", + "memberName": "Member Name", + "memberPosition": "Position", + "memberTeam": [ + "Cerc", + "Laconic" + ], + "memberTwitter": "https://twitter.com/twitter-handle", + "id": "12345678", + "memberImage": { + "url": "/images/site_content/team/_default.jpg" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/team/ashwin-phatak.json b/json/site_content/team/ashwin-phatak.json new file mode 100644 index 0000000..48a5828 --- /dev/null +++ b/json/site_content/team/ashwin-phatak.json @@ -0,0 +1,18 @@ +{ + "data": { + "team": { + "memberGithub": "https://github.com/ashwinphatak", + "memberLinkedin": "https://www.linkedin.com/in/ashwinphatak/", + "memberName": "Ashwin Phatak", + "memberPosition": "Chief Architect", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "https://twitter.com/ashwinphatak", + "id": "14097767", + "memberImage": { + "url": "/images/site_content/team/ashwin-phatak.jpg" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/team/erik-dies.json b/json/site_content/team/erik-dies.json new file mode 100644 index 0000000..06172ea --- /dev/null +++ b/json/site_content/team/erik-dies.json @@ -0,0 +1,18 @@ +{ + "data": { + "team": { + "memberGithub": "https://github.com/erikdies", + "memberLinkedin": "", + "memberName": "Erik Dies", + "memberPosition": "Product Lead", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "https://twitter.com/thelesserdies", + "id": "14097771", + "memberImage": { + "url": "/images/site_content/team/erik-dies.jpg" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/team/ian-norden.json b/json/site_content/team/ian-norden.json new file mode 100644 index 0000000..31240ff --- /dev/null +++ b/json/site_content/team/ian-norden.json @@ -0,0 +1,18 @@ +{ + "data": { + "team": { + "memberGithub": "https://github.com/i-norden", + "memberLinkedin": "https://www.linkedin.com/in/ian-s-norden/", + "memberName": "Ian Norden", + "memberPosition": "VP of Engineering", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "", + "id": "14097768", + "memberImage": { + "url": "/images/site_content/team/ian-norden.jpg" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/team/leah-light.json b/json/site_content/team/leah-light.json new file mode 100644 index 0000000..553c7cb --- /dev/null +++ b/json/site_content/team/leah-light.json @@ -0,0 +1,18 @@ +{ + "data": { + "team": { + "memberGithub": "", + "memberLinkedin": "https://www.linkedin.com/in/leah-light-a308535/", + "memberName": "Leah Light", + "memberPosition": "Operations Manager", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "", + "id": "14097770", + "memberImage": { + "url": "/images/site_content/team/leah-light.jpg" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/team/rick-dudley.json b/json/site_content/team/rick-dudley.json new file mode 100644 index 0000000..f2ebcfa --- /dev/null +++ b/json/site_content/team/rick-dudley.json @@ -0,0 +1,18 @@ +{ + "data": { + "team": { + "memberGithub": "https://github.com/AFDudley", + "memberLinkedin": "https://www.linkedin.com/in/afdudley/", + "memberName": "Rick Dudley", + "memberPosition": "President", + "memberTeam": [ + "Cerc" + ], + "memberTwitter": "https://twitter.com/AFDudley0", + "id": "14097746", + "memberImage": { + "url": "/images/site_content/team/rick-dudley.jpg" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/team/seven-founding-members.json b/json/site_content/team/seven-founding-members.json new file mode 100644 index 0000000..7d283a1 --- /dev/null +++ b/json/site_content/team/seven-founding-members.json @@ -0,0 +1,18 @@ +{ + "data": { + "team": { + "memberGithub": "", + "memberLinkedin": "", + "memberName": "7 Founding Members", + "memberPosition": "Announcing in Q3/2022", + "memberTeam": [ + "Laconic" + ], + "memberTwitter": "", + "id": "41347341", + "memberImage": { + "url": "/images/site_content/team/seven-founding-members.png" + } + } + } +} \ No newline at end of file diff --git a/json/site_content/termsPage.json b/json/site_content/termsPage.json new file mode 100644 index 0000000..b84530e --- /dev/null +++ b/json/site_content/termsPage.json @@ -0,0 +1,720 @@ +{ + "data": { + "termsPage": { + "termsContent": { + "value": { + "schema": "dast", + "document": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Terms of Use" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Please read these terms of use carefully before using Our Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Interpretation and Definitions" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Interpretation" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Definitions" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "For the purposes of these Terms of Use:" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Affiliate" + }, + { + "type": "span", + "value": " means an entity that controls, is controlled by or is under common control with a party, where \"control\" means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Country" + }, + { + "type": "span", + "value": " refers to United States." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Company" + }, + { + "type": "span", + "value": " (referred to as either \"the Company\", \"We\", \"Us\" or \"Our\" in this Agreement) refers to Xylm Inc." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Device" + }, + { + "type": "span", + "value": " means any device that can access the Service such as a computer, a cellphone or a digital tablet." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Service" + }, + { + "type": "span", + "value": " refers to the Website." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Terms of Use" + }, + { + "type": "span", + "value": " (also referred as \"Terms\") mean these Terms of Use that form the entire agreement between You and the Company regarding the use of the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Third-party Social Media Service" + }, + { + "type": "span", + "value": " means any services or content (including data, information, products or services) provided by a third-party that may be displayed, included or made available by the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Website" + }, + { + "type": "span", + "value": " refers to Laconic, accessible from " + }, + { + "url": "https://www.laconic.com/", + "type": "link", + "children": [ + { + "type": "span", + "value": "https://www.laconic.com/" + } + ] + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "You" + }, + { + "type": "span", + "value": " means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Acknowledgment" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These are the Terms of Use governing the use of this Service and the agreement that operates between You and the Company. These Terms of Use set out the rights and obligations of all users regarding the use of the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your access to and use of the Service is conditioned on Your acceptance of and compliance with these Terms of Use. These Terms of Use apply to all visitors, users and others who access or use the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "By accessing or using the Service You agree to be bound by these Terms of Use. If You disagree with any part of these Terms of Use then You may not access the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You represent that you are over the age of 18. The Company does not permit those under 18 to use the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Your access to and use of the Service is also conditioned on Your acceptance of and compliance with the Privacy Policy of the Company. Our Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your personal information when You use the Application or the Website and tells You about Your privacy rights and how the law protects You. Please read Our Privacy Policy carefully before using Our Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Intellectual Property" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Service and its original content (excluding Content provided by You or other users), features and functionality are and will remain the exclusive property of the Company and its licensors." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Service is protected by copyright, trademark, and other laws of both the Country and foreign countries." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our trademarks and trade dress may not be used in connection with any product or service without the prior written consent of the Company." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Links to Other Websites" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Our Service may contain links to third-party web sites or services that are not owned or controlled by the Company." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Company has no control over, and assumes no responsibility for, the content, privacy policies, or practices of any third party web sites or services. You further acknowledge and agree that the Company shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such content, goods or services available on or through any such web sites or services." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We strongly advise You to read the terms of use and privacy policies of any third-party web sites or services that You visit." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Termination" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We may terminate or suspend Your access immediately, without prior notice or liability, for any reason whatsoever, including without limitation if You breach these Terms of Use." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Upon termination, Your right to use the Service will cease immediately." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Limitation of Liability" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Notwithstanding any damages that You might incur, the entire liability of the Company and any of its suppliers under any provision of this Terms and Your exclusive remedy for all of the foregoing shall be limited to the amount actually paid by You through the Service or 100 USD if You haven't purchased anything through the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "To the maximum extent permitted by applicable law, in no event shall the Company or its suppliers be liable for any special, incidental, indirect, or consequential damages whatsoever (including, but not limited to, damages for loss of profits, loss of data or other information, for business interruption, for personal injury, loss of privacy arising out of or in any way related to the use of or inability to use the Service, third-party software and/or third-party hardware used with the Service, or otherwise in connection with any provision of this Terms), even if the Company or any supplier has been advised of the possibility of such damages and even if the remedy fails of its essential purpose." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Some states do not allow the exclusion of implied warranties or limitation of liability for incidental or consequential damages, which means that some of the above limitations may not apply. In these states, each party's liability will be limited to the greatest extent permitted by law." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "\"AS IS\" and \"AS AVAILABLE\" Disclaimer" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The Service is provided to You \"AS IS\" and \"AS AVAILABLE\" and with all faults and defects without warranty of any kind. To the maximum extent permitted under applicable law, the Company, on its own behalf and on behalf of its Affiliates and its and their respective licensors and service providers, expressly disclaims all warranties, whether express, implied, statutory or otherwise, with respect to the Service, including all implied warranties of merchantability, fitness for a particular purpose, title and non-infringement, and warranties that may arise out of course of dealing, course of performance, usage or trade practice. Without limitation to the foregoing, the Company provides no warranty or undertaking, and makes no representation of any kind that the Service will meet Your requirements, achieve any intended results, be compatible or work with any other software, applications, systems or services, operate without interruption, meet any performance or reliability standards or be error free or that any errors or defects can or will be corrected." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Without limiting the foregoing, neither the Company nor any of the company's provider makes any representation or warranty of any kind, express or implied: (i) as to the operation or availability of the Service, or the information, content, and materials or products included thereon; (ii) that the Service will be uninterrupted or error-free; (iii) as to the accuracy, reliability, or currency of any information or content provided through the Service; or (iv) that the Service, its servers, the content, or e-mails sent from or on behalf of the Company are free of viruses, scripts, trojan horses, worms, malware, timebombs or other harmful components." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Some jurisdictions do not allow the exclusion of certain types of warranties or limitations on applicable statutory rights of a consumer, so some or all of the above exclusions and limitations may not apply to You. But in such a case the exclusions and limitations set forth in this section shall be applied to the greatest extent enforceable under applicable law." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Governing Law" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "The laws of the Country, excluding its conflicts of law rules, shall govern this Terms and Your use of the Service. Your use of the Application may also be subject to other local, state, national, or international laws." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Disputes Resolution" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If You have any concern or dispute about the Service, You agree to first try to resolve the dispute informally by contacting the Company." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "For European Union (EU) Users" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If You are a European Union consumer, you will benefit from any mandatory provisions of the law of the country in which you are resident in." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "United States Legal Compliance" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "You represent and warrant that (i) You are not located in a country that is subject to the United States government embargo, or that has been designated by the United States government as a \"terrorist supporting\" country, and (ii) You are not listed on any United States government list of prohibited or restricted parties." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Severability and Waiver" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Severability" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If any provision of these Terms is held to be unenforceable or invalid, such provision will be changed and interpreted to accomplish the objectives of such provision to the greatest extent possible under applicable law and the remaining provisions will continue in full force and effect." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Waiver" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "Except as provided herein, the failure to exercise a right or to require performance of an obligation under these Terms shall not effect a party's ability to exercise such right or require such performance at any time thereafter nor shall the waiver of a breach constitute a waiver of any subsequent breach." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Translation Interpretation" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "These Terms of Use may have been translated if We have made them available to You on our Service. You agree that the original English text shall prevail in the case of a dispute." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Changes to These Terms of Use" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "We reserve the right, at Our sole discretion, to modify or replace these Terms at any time. If a revision is material We will make reasonable efforts to provide at least 30 days' notice prior to any new terms taking effect. What constitutes a material change will be determined at Our sole discretion." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "By continuing to access or use Our Service after those revisions become effective, You agree to be bound by the revised terms. If You do not agree to the new terms, in whole or in part, please stop using the website and the Service." + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "marks": [ + "strong" + ], + "value": "Contact Us" + } + ] + }, + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "If you have any questions about these Terms of Use, You can contact us:" + } + ] + }, + { + "type": "list", + "style": "bulleted", + "children": [ + { + "type": "listItem", + "children": [ + { + "type": "paragraph", + "children": [ + { + "type": "span", + "value": "By email: info@xylm.io" + } + ] + } + ] + } + ] + } + ] + } + }, + "blocks": [] + }, + "_updatedAt": "2022-08-15T21:55:57-07:00" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/_testimonial.json b/json/site_content/testimonial/_testimonial.json new file mode 100644 index 0000000..991d475 --- /dev/null +++ b/json/site_content/testimonial/_testimonial.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "12345678", + "testimonialImage": { + "url": "https://www.datocms-assets.com/66113/1660570305-t1qh-epl_400x400.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "Testimonail Plain Text\n(New lines are respected).", + "testimonialUsername": "@userHandle" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/cjremus.json b/json/site_content/testimonial/cjremus.json new file mode 100644 index 0000000..e2bf933 --- /dev/null +++ b/json/site_content/testimonial/cjremus.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53682905", + "testimonialImage": { + "url": "/images/site_content/testimonial/cjremus.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "I feel hopeful for and inspired by what @laconicnetwork is building 🌟", + "testimonialUsername": "@cjremus" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/danfinlay.json b/json/site_content/testimonial/danfinlay.json new file mode 100644 index 0000000..b4a1038 --- /dev/null +++ b/json/site_content/testimonial/danfinlay.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53681948", + "testimonialImage": { + "url": "/images/site_content/testimonial/danfinlay.jpeg" + }, + "testimonialPosition": "MetaMask Founder", + "testimonialText": "Once we have good end-user caching of partial blockchain state (Like via \n@laconicnetwork), we could enable users to do lookups on a local phishing list, gaining stronger privacy benefits for much lower cost than a full node.", + "testimonialUsername": "@Danfinlay" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/dougiedeluca.json b/json/site_content/testimonial/dougiedeluca.json new file mode 100644 index 0000000..5c87940 --- /dev/null +++ b/json/site_content/testimonial/dougiedeluca.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53676939", + "testimonialImage": { + "url": "/images/site_content/testimonial/dougiedeluca.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "Here’s a few of my favorite slept on protocols at the moment:\n\n@ComposableFin\n@laconicnetwork\n@archwayHQ\n@SeiNetwork\n@Shade_Protocol\n \nEach doing industry leading work in their respective niche and poised to gain significant market share during the bear market and beyond.", + "testimonialUsername": "@DougieDeLuca" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/dystophiabreaker.json b/json/site_content/testimonial/dystophiabreaker.json new file mode 100644 index 0000000..3ab7ffa --- /dev/null +++ b/json/site_content/testimonial/dystophiabreaker.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53682908", + "testimonialImage": { + "url": "/images/site_content/testimonial/dystophiabreaker.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "shoutout to @cosmosibc and @nomadxyz_, and @agoric and @laconicnetwork", + "testimonialUsername": "@dystophiabreaker" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/ether_gavin.json b/json/site_content/testimonial/ether_gavin.json new file mode 100644 index 0000000..9e2f5f7 --- /dev/null +++ b/json/site_content/testimonial/ether_gavin.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53682912", + "testimonialImage": { + "url": "/images/site_content/testimonial/ether_gavin.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "if you're also wondering how we level up (from ground-level projects), @laconicnetwork looks like a compelling middleware layer to me 👀", + "testimonialUsername": "@Ether_Gavin" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/kumavis.json b/json/site_content/testimonial/kumavis.json new file mode 100644 index 0000000..a9fc872 --- /dev/null +++ b/json/site_content/testimonial/kumavis.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53682921", + "testimonialImage": { + "url": "/images/site_content/testimonial/kumavis.jpg" + }, + "testimonialPosition": "", + "testimonialText": "Running infura for free is actually extremely expensive, we pursued a light client before but it required a fresh implementation. hopefully @laconicnetwork solves this problem", + "testimonialUsername": "@kumavis" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/motherpredicate.json b/json/site_content/testimonial/motherpredicate.json new file mode 100644 index 0000000..179d06c --- /dev/null +++ b/json/site_content/testimonial/motherpredicate.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53682698", + "testimonialImage": { + "url": "/images/site_content/testimonial/motherpredicate.jpeg" + }, + "testimonialPosition": "", + "testimonialText": "excited about @laconicnetwork\n \nthis is what actual free market mutualism can create - incentive structures around maintaining tailored state data that can then be made valuable to others as a self-sustaining public good.", + "testimonialUsername": "@motherpredicate" + } + } +} \ No newline at end of file diff --git a/json/site_content/testimonial/rickmanelius.json b/json/site_content/testimonial/rickmanelius.json new file mode 100644 index 0000000..c7a8074 --- /dev/null +++ b/json/site_content/testimonial/rickmanelius.json @@ -0,0 +1,13 @@ +{ + "data": { + "testimonial": { + "id": "53682920", + "testimonialImage": { + "url": "/images/site_content/testimonial/rickmanelius.jpg" + }, + "testimonialPosition": "", + "testimonialText": "If you love web3 infra layer tools/solutions, give @laconicnetwork a follow. I'm incredibly excited with what they have coming down the pipeline…\n", + "testimonialUsername": "@rickmanelius" + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 76270f8..faddf11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11791 +1,8 @@ { "name": "laconic", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "laconic", - "version": "1.0.0", - "dependencies": { - "@graphql-codegen/introspection": "^2.1.1", - "@graphql-codegen/typescript-graphql-request": "^4.4.5", - "@graphql-codegen/typescript-operations": "^2.3.5", - "@hubspot/api-client": "^7.1.2", - "@juggle/resize-observer": "^3.3.1", - "@notionhq/client": "^1.0.4", - "@radix-ui/react-navigation-menu": "^0.1.2", - "@radix-ui/react-polymorphic": "^0.0.14", - "@reach/dialog": "^0.17.0", - "@types/lodash": "^4.14.182", - "@types/marked": "^4.0.3", - "clsx": "^1.1.1", - "datocms-structured-text-to-html-string": "^2.0.4", - "datocms-structured-text-to-plain-text": "^2.0.4", - "graphql": "^16.3.0", - "graphql-request": "^4.2.0", - "gsap": "./src/lib/gsap/gsap-bonus.tgz", - "keen-slider": "^6.6.9", - "lodash": "^4.17.21", - "marked": "^4.0.14", - "next": "^12.2.5", - "next-real-viewport": "^0.7.0", - "next-seo": "^5.4.0", - "next-themes": "^0.2.0", - "react": "^18.2.0", - "react-datocms": "^3.0.12", - "react-device-detect": "^2.2.2", - "react-dom": "^18.2.0", - "react-fast-marquee": "^1.3.1", - "react-google-recaptcha": "^2.1.0", - "react-google-recaptcha-v3": "^1.10.0", - "react-hook-form": "^7.30.0", - "react-mailchimp-subscribe": "^2.1.3", - "react-merge-refs": "^1.1.0", - "react-query": "^3.35.0", - "react-select": "^5.3.2", - "react-use-measure": "^2.1.1", - "react-youtube": "^9.0.2", - "sharp": "^0.30.4", - "tiny-json-http": "^7.4.2", - "zod": "^3.17.3" - }, - "devDependencies": { - "@graphql-codegen/cli": "^2.6.2", - "@next/bundle-analyzer": "^12.1.5", - "@types/css-font-loading-module": "0.0.7", - "@types/node": "^17.0.25", - "@types/react": "^17.0.43", - "@types/react-dom": "^17.0.14", - "@types/react-google-recaptcha": "^2.1.5", - "@types/tiny-json-http": "^7.3.1", - "@typescript-eslint/eslint-plugin": "^5.20.0", - "@typescript-eslint/parser": "^5.20.0", - "cross-env": "^7.0.3", - "eslint": "^8.13.0", - "eslint-config-next": "^12.1.5", - "eslint-config-prettier": "8.5.0", - "eslint-import-resolver-typescript": "2.7.1", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react": "7.29.4", - "eslint-plugin-react-hooks": "4.4.0", - "eslint-plugin-simple-import-sort": "^7.0.0", - "husky": "^7.0.4", - "lint-staged": "^12.3.8", - "next-compose-plugins": "^2.2.1", - "next-sitemap": "^2.5.20", - "prettier": "2.6.2", - "sass": "^1.50.1", - "stylelint": "^14.7.1", - "stylelint-config-prettier": "^9.0.3", - "stylelint-config-standard": "^25.0.0", - "stylelint-config-standard-scss": "^3.0.0", - "stylelint-prettier": "^2.0.0", - "typescript": "^4.8.2" - }, - "engines": { - "node": ">=14.x", - "yarn": "1.x" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", - "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", - "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.13", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.13", - "@babel/types": "^7.18.13", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", - "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", - "dependencies": { - "@babel/types": "^7.18.13", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", - "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", - "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz", - "integrity": "sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-member-expression-to-functions": "^7.17.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", - "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", - "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", - "dependencies": { - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", - "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.17.12", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz", - "integrity": "sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", - "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-member-expression-to-functions": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", - "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", - "dependencies": { - "@babel/types": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", - "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0= sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", - "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", - "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", - "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", - "dependencies": { - "@babel/compat-data": "^7.17.0", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz", - "integrity": "sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.17.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz", - "integrity": "sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.17.12" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", - "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", - "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", - "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", - "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-optimise-call-expression": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", - "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", - "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz", - "integrity": "sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-flow": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", - "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", - "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", - "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", - "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz", - "integrity": "sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw==", - "dependencies": { - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.17.7", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", - "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-replace-supers": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", - "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", - "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz", - "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz", - "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.16.7", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", - "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", - "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", - "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz", - "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==", - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.8.tgz", - "integrity": "sha512-ZbYSUvoSF6dXZmMl/CYTMOvzIFnbGfv4W3SEHYgMvNsFTeLaF2gkGAF4K2ddmtSK4Emej+0aYcnSC6N5dPCXUQ==", - "dev": true, - "dependencies": { - "core-js-pure": "^3.20.2", - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", - "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.13", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.13", - "@babel/types": "^7.18.13", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.18.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", - "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", - "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@corex/deepmerge": { - "version": "2.6.148", - "resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-2.6.148.tgz", - "integrity": "sha512-6QMz0/2h5C3ua51iAnXMPWFbb1QOU1UvSM4bKBw5mzdT+WtLgjbETBBIQZ+Sh9WvEcGwlAt/DEdRpIC3XlDBMA==", - "dev": true - }, - "node_modules/@emotion/babel-plugin": { - "version": "11.9.2", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz", - "integrity": "sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==", - "dependencies": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/runtime": "^7.13.10", - "@emotion/hash": "^0.8.0", - "@emotion/memoize": "^0.7.5", - "@emotion/serialize": "^1.0.2", - "babel-plugin-macros": "^2.6.1", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.0.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz", - "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==", - "dependencies": { - "@emotion/memoize": "^0.7.4", - "@emotion/sheet": "^1.1.0", - "@emotion/utils": "^1.0.0", - "@emotion/weak-memoize": "^0.2.5", - "stylis": "4.0.13" - } - }, - "node_modules/@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" - }, - "node_modules/@emotion/memoize": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", - "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" - }, - "node_modules/@emotion/react": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.0.tgz", - "integrity": "sha512-lBVSF5d0ceKtfKCDQJveNAtkC7ayxpVlgOohLgXqRwqWr9bOf4TZAFFyIcNngnV6xK6X4x2ZeXq7vliHkoVkxQ==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@emotion/babel-plugin": "^11.7.1", - "@emotion/cache": "^11.7.1", - "@emotion/serialize": "^1.0.3", - "@emotion/utils": "^1.1.0", - "@emotion/weak-memoize": "^0.2.5", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz", - "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==", - "dependencies": { - "@emotion/hash": "^0.8.0", - "@emotion/memoize": "^0.7.4", - "@emotion/unitless": "^0.7.5", - "@emotion/utils": "^1.0.0", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz", - "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==" - }, - "node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" - }, - "node_modules/@emotion/utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz", - "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", - "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" - }, - "node_modules/@endemolshinegroup/cosmiconfig-typescript-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz", - "integrity": "sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==", - "dev": true, - "dependencies": { - "lodash.get": "^4", - "make-error": "^1", - "ts-node": "^9", - "tslib": "^2" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "cosmiconfig": ">=6" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", - "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.1", - "globals": "^13.9.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@graphql-codegen/cli": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-2.6.2.tgz", - "integrity": "sha512-UO75msoVgvLEvfjCezM09cQQqp32+mR8Ma1ACsBpr7nroFvHbgcu2ulx1cMovg4sxDBCsvd9Eq/xOOMpARUxtw==", - "dev": true, - "dependencies": { - "@graphql-codegen/core": "2.5.1", - "@graphql-codegen/plugin-helpers": "^2.4.1", - "@graphql-tools/apollo-engine-loader": "^7.0.5", - "@graphql-tools/code-file-loader": "^7.0.6", - "@graphql-tools/git-loader": "^7.0.5", - "@graphql-tools/github-loader": "^7.0.5", - "@graphql-tools/graphql-file-loader": "^7.0.5", - "@graphql-tools/json-file-loader": "^7.1.2", - "@graphql-tools/load": "^7.3.0", - "@graphql-tools/prisma-loader": "^7.0.6", - "@graphql-tools/url-loader": "^7.0.11", - "@graphql-tools/utils": "^8.1.1", - "ansi-escapes": "^4.3.1", - "chalk": "^4.1.0", - "change-case-all": "1.0.14", - "chokidar": "^3.5.2", - "common-tags": "^1.8.0", - "cosmiconfig": "^7.0.0", - "debounce": "^1.2.0", - "dependency-graph": "^0.11.0", - "detect-indent": "^6.0.0", - "glob": "^7.1.6", - "globby": "^11.0.4", - "graphql-config": "^4.1.0", - "inquirer": "^8.0.0", - "is-glob": "^4.0.1", - "json-to-pretty-yaml": "^1.2.2", - "latest-version": "5.1.0", - "listr": "^0.14.3", - "listr-update-renderer": "^0.5.0", - "log-symbols": "^4.0.0", - "minimatch": "^4.0.0", - "mkdirp": "^1.0.4", - "string-env-interpolation": "^1.0.1", - "ts-log": "^2.2.3", - "tslib": "~2.3.0", - "valid-url": "^1.0.9", - "wrap-ansi": "^7.0.0", - "yaml": "^1.10.0", - "yargs": "^17.0.0" - }, - "bin": { - "gql-gen": "bin.js", - "graphql-code-generator": "bin.js", - "graphql-codegen": "bin.js" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-codegen/core": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-2.5.1.tgz", - "integrity": "sha512-alctBVl2hMnBXDLwkgmnFPrZVIiBDsWJSmxJcM4GKg1PB23+xuov35GE47YAyAhQItE1B1fbYnbb1PtGiDZ4LA==", - "dev": true, - "dependencies": { - "@graphql-codegen/plugin-helpers": "^2.4.1", - "@graphql-tools/schema": "^8.1.2", - "@graphql-tools/utils": "^8.1.1", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-codegen/introspection": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/introspection/-/introspection-2.1.1.tgz", - "integrity": "sha512-O9zsy0IoFYDo37pBVF4pSvRMDx/AKdgOxyko4R/O+0DHEw9Nya/pQ3dbn+LDLj2n6X+xOXUBUfFvqhODTqU28w==", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^2.3.2", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-codegen/plugin-helpers": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.4.2.tgz", - "integrity": "sha512-LJNvwAPv/sKtI3RnRDm+nPD+JeOfOuSOS4FFIpQCMUCyMnFcchV/CPTTv7tT12fLUpEg6XjuFfDBvOwndti30Q==", - "dependencies": { - "@graphql-tools/utils": "^8.5.2", - "change-case-all": "1.0.14", - "common-tags": "1.8.2", - "import-from": "4.0.0", - "lodash": "~4.17.0", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-codegen/schema-ast": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-2.4.1.tgz", - "integrity": "sha512-bIWlKk/ShoVJfghA4Rt1OWnd34/dQmZM/vAe6fu6QKyOh44aAdqPtYQ2dbTyFXoknmu504etKJGEDllYNUJRfg==", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^2.3.2", - "@graphql-tools/utils": "^8.1.1", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-codegen/typescript": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-2.4.8.tgz", - "integrity": "sha512-tVsHIkuyenBany7c5IMU1yi4S1er2hgyXJGNY7PcyhpJMx0eChmbqz1VTiZxDEwi8mDBS2mn3TaSJMh6xuJM5g==", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^2.4.0", - "@graphql-codegen/schema-ast": "^2.4.1", - "@graphql-codegen/visitor-plugin-common": "2.7.4", - "auto-bind": "~4.0.0", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-codegen/typescript-graphql-request": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-graphql-request/-/typescript-graphql-request-4.4.5.tgz", - "integrity": "sha512-D8ao8j43R8pStLqCMXVzdvGX9xc/x+IWd8ZacbKUVKbpWxlO7AEVAF89fgTpPv4FFTYG62BdkElf3ePcvgEWuw==", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^2.4.0", - "@graphql-codegen/visitor-plugin-common": "2.7.4", - "auto-bind": "~4.0.0", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", - "graphql-request": "^3.4.0 || ^4.0.0", - "graphql-tag": "^2.0.0" - } - }, - "node_modules/@graphql-codegen/typescript-operations": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-2.3.5.tgz", - "integrity": "sha512-GCZQW+O+cIF62ioPkQMoSJGzjJhtr7ttZGJOAoN/Q/oolG8ph9jNFePKO67tSQ/POAs5HLqfat4kAlCK8OPV3Q==", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^2.4.0", - "@graphql-codegen/typescript": "^2.4.8", - "@graphql-codegen/visitor-plugin-common": "2.7.4", - "auto-bind": "~4.0.0", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.7.4.tgz", - "integrity": "sha512-aaDoEudDD+B7DK/UwDSL2Fzej75N9hNJ3N8FQuTIeDyw6FNGWUxmkjVBLQGlzfnYfK8IYkdfYkrPn3Skq0pVxA==", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^2.4.0", - "@graphql-tools/optimize": "^1.0.1", - "@graphql-tools/relay-operation-optimizer": "^6.3.7", - "@graphql-tools/utils": "^8.3.0", - "auto-bind": "~4.0.0", - "change-case-all": "1.0.14", - "dependency-graph": "^0.11.0", - "graphql-tag": "^2.11.0", - "parse-filepath": "^1.0.2", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/apollo-engine-loader": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.2.7.tgz", - "integrity": "sha512-FNX8QW2BeYyFNk+UX4pn6zaiK1NnMfUMeU3ZYYt2dvJT4tFCt/tanmXZbQ1+YSvnH0j6PU0eKoeJTcYVK3f4Fw==", - "dev": true, - "dependencies": { - "@graphql-tools/utils": "8.6.6", - "cross-undici-fetch": "^0.1.19", - "sync-fetch": "0.3.1", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/batch-execute": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-8.4.2.tgz", - "integrity": "sha512-5/el640oG/jfjQCjCRDdtIALyUib8YPONM2NSmckp2g1nOrPTAx/isz3Uptp9y5OI1UXXhONiKy5euTbgsGoXw==", - "dev": true, - "dependencies": { - "@graphql-tools/utils": "8.6.6", - "dataloader": "2.0.0", - "tslib": "~2.3.0", - "value-or-promise": "1.0.11" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/code-file-loader": { - "version": "7.2.11", - "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-7.2.11.tgz", - "integrity": "sha512-W3iOwGb3yR8DYXBB0RE5X+jo8ExNzYUUhDP2nB1XDgDoOylfN+WNzPcWfTczTd9SEXqvbjSa5UqH7dv37S491A==", - "dev": true, - "dependencies": { - "@graphql-tools/graphql-tag-pluck": "7.2.3", - "@graphql-tools/utils": "8.6.6", - "globby": "^11.0.3", - "tslib": "~2.3.0", - "unixify": "^1.0.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/delegate": { - "version": "8.7.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-8.7.2.tgz", - "integrity": "sha512-SSmx5N6Cq23KRT0YepdmcYugey7MDZSXxtJ8KHHdc5eW9IAHXZWsJWdVnI9woU9omsnE6svnxblZb1UUBl7AUg==", - "dev": true, - "dependencies": { - "@graphql-tools/batch-execute": "8.4.2", - "@graphql-tools/schema": "8.3.7", - "@graphql-tools/utils": "8.6.6", - "dataloader": "2.0.0", - "graphql-executor": "0.0.22", - "tslib": "~2.3.0", - "value-or-promise": "1.0.11" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/git-loader": { - "version": "7.1.10", - "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-7.1.10.tgz", - "integrity": "sha512-v6kyHVg6I5GMjDlcLhBwcvFyJFOCbt5azaEvCq6UPHeziVmFK96gpvHdONfxLIj5k7lpNxodL5zJ2TlDQuOfbQ==", - "dev": true, - "dependencies": { - "@graphql-tools/graphql-tag-pluck": "7.2.3", - "@graphql-tools/utils": "8.6.6", - "is-glob": "4.0.3", - "micromatch": "^4.0.4", - "tslib": "~2.3.0", - "unixify": "^1.0.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/github-loader": { - "version": "7.2.11", - "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-7.2.11.tgz", - "integrity": "sha512-OKYViepaFd2YFm8Du/CWP70Wh9JqKyHLdYt48InmQTEQGPlMXxrxztY127d/eDo5uW0Osp77G0d34fFjxD8XKg==", - "dev": true, - "dependencies": { - "@graphql-tools/graphql-tag-pluck": "7.2.3", - "@graphql-tools/utils": "8.6.6", - "cross-undici-fetch": "^0.1.19", - "sync-fetch": "0.3.1", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/graphql-file-loader": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.3.8.tgz", - "integrity": "sha512-SpQZQ0klbox/kxYCLFBTmhLuQFm7P6usWVIqwROK4JSomwCuccc2zDsr1H7ayDpanD3yfkzMsl6gPkOkAo52pA==", - "dev": true, - "dependencies": { - "@graphql-tools/import": "6.6.10", - "@graphql-tools/utils": "8.6.6", - "globby": "^11.0.3", - "tslib": "~2.3.0", - "unixify": "^1.0.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/graphql-tag-pluck": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.2.3.tgz", - "integrity": "sha512-rk6R98ZWeZK63W2jQWwnFZGcQUrbj/Dl3ok0m2DxHTqjHwhRdykY5o1lEX7SzFqGXjs7WE5tUpvlzswAKmklFw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.16.8", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8", - "@graphql-tools/utils": "8.6.6", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/import": { - "version": "6.6.10", - "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-6.6.10.tgz", - "integrity": "sha512-yHdlEPTvIjrngtQFNgkMQJt/DjG3hQKvc6Mb8kaatFV4yERN5zx+0vpdrwxTwRNG1N7bI/YCkbrc7PXOb+g89Q==", - "dev": true, - "dependencies": { - "@graphql-tools/utils": "8.6.6", - "resolve-from": "5.0.0", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/json-file-loader": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-7.3.8.tgz", - "integrity": "sha512-W3nVLAp8m787A17wja7ysayij7WMRu+lF8LeCWr9eoyiCuw65i63y0G4eqZ5+Q0+E2BYWlKJyk/Z0vsFVJGMUA==", - "dev": true, - "dependencies": { - "@graphql-tools/utils": "8.6.6", - "globby": "^11.0.3", - "tslib": "~2.3.0", - "unixify": "^1.0.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/load": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-7.5.7.tgz", - "integrity": "sha512-Z4oKf4MdBvl0EyubmvPL14ldhovKz8C61rQPHD8pjnC8Z0RbvW0a/sns/yuHuCVZoJMsSboU65DPzPTIoQUM4w==", - "dev": true, - "dependencies": { - "@graphql-tools/schema": "8.3.7", - "@graphql-tools/utils": "8.6.6", - "p-limit": "3.1.0", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/merge": { - "version": "8.2.7", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.2.7.tgz", - "integrity": "sha512-rKxjNogqu1UYAG/y5FOb6lJsmSQbWA+jq4inWjNEVX54VGGE7/WGnmPaqcsyomNOfS3vIRS6NnG+DxiQSqetjg==", - "dev": true, - "dependencies": { - "@graphql-tools/utils": "8.6.6", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/optimize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-1.2.0.tgz", - "integrity": "sha512-l0PTqgHeorQdeOizUor6RB49eOAng9+abSxiC5/aHRo6hMmXVaqv5eqndlmxCpx9BkgNb3URQbK+ZZHVktkP/g==", - "dependencies": { - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/prisma-loader": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-7.1.8.tgz", - "integrity": "sha512-lOqCfGGDrK0KmNkFQA0N2lD8idVLhPd7HlznRfIje+NiLeftHuBtzLPvoNwOBcwWLgyYxHIkE30WatyK52VCCQ==", - "dev": true, - "dependencies": { - "@graphql-tools/url-loader": "7.9.9", - "@graphql-tools/utils": "8.6.6", - "@types/js-yaml": "^4.0.0", - "@types/json-stable-stringify": "^1.0.32", - "@types/jsonwebtoken": "^8.5.0", - "chalk": "^4.1.0", - "debug": "^4.3.1", - "dotenv": "^16.0.0", - "graphql-request": "^4.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "isomorphic-fetch": "^3.0.0", - "js-yaml": "^4.0.0", - "json-stable-stringify": "^1.0.1", - "jsonwebtoken": "^8.5.1", - "lodash": "^4.17.20", - "replaceall": "^0.1.6", - "scuid": "^1.1.0", - "tslib": "~2.3.0", - "yaml-ast-parser": "^0.0.43" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.4.6.tgz", - "integrity": "sha512-RJ2zoVUGPXbs1zdFjY56YWGsJaG9RyTOk5wlFe369ts+iXnzpM0ezszLfOjggtXUPlVTbjWNmdHASOh3rZ8uBA==", - "dependencies": { - "@graphql-tools/utils": "8.6.6", - "relay-compiler": "12.0.0", - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/immutable": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", - "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks= sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/relay-compiler": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/relay-compiler/-/relay-compiler-12.0.0.tgz", - "integrity": "sha512-SWqeSQZ+AMU/Cr7iZsHi1e78Z7oh00I5SvR092iCJq79aupqJ6Ds+I1Pz/Vzo5uY5PY0jvC4rBJXzlIN5g9boQ==", - "dependencies": { - "@babel/core": "^7.14.0", - "@babel/generator": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/runtime": "^7.0.0", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.0.0", - "babel-preset-fbjs": "^3.4.0", - "chalk": "^4.0.0", - "fb-watchman": "^2.0.0", - "fbjs": "^3.0.0", - "glob": "^7.1.1", - "immutable": "~3.7.6", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "relay-runtime": "12.0.0", - "signedsource": "^1.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "relay-compiler": "bin/relay-compiler" - }, - "peerDependencies": { - "graphql": "^15.0.0" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-tools/relay-operation-optimizer/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@graphql-tools/schema": { - "version": "8.3.7", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.3.7.tgz", - "integrity": "sha512-7byr9J6rfMPFPfiR4u65dy20xHATTvbgOY7KYd1sYPnMKKfRZe0tUgpnE+noXcfob7N8s366WaVh7bEoztQMwg==", - "dev": true, - "dependencies": { - "@graphql-tools/merge": "8.2.7", - "@graphql-tools/utils": "8.6.6", - "tslib": "~2.3.0", - "value-or-promise": "1.0.11" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/url-loader": { - "version": "7.9.9", - "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-7.9.9.tgz", - "integrity": "sha512-qhjBJ3oCXZrzvJchVwtrahr48TXOHPYZ4YXklGrbJVoJs3LP0a7CYUwuXeiNuN+dpgaxkb175sIEN9m0FadGRw==", - "dev": true, - "dependencies": { - "@graphql-tools/delegate": "8.7.2", - "@graphql-tools/utils": "8.6.6", - "@graphql-tools/wrap": "8.4.11", - "@n1ru4l/graphql-live-query": "^0.9.0", - "@types/websocket": "^1.0.4", - "@types/ws": "^8.0.0", - "cross-undici-fetch": "^0.1.19", - "dset": "^3.1.0", - "extract-files": "^11.0.0", - "graphql-sse": "^1.0.1", - "graphql-ws": "^5.4.1", - "isomorphic-ws": "^4.0.1", - "meros": "^1.1.4", - "subscriptions-transport-ws": "^0.11.0", - "sync-fetch": "^0.3.1", - "tslib": "^2.3.0", - "value-or-promise": "^1.0.11", - "ws": "^8.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/utils": { - "version": "8.6.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.6.6.tgz", - "integrity": "sha512-wjY2ljKLCnnbRrDNPPgPNqCujou0LFSOWcxAjV6DYUlfFWTsAEvlYmsmY4T+K12wI/fnqoJ2bUwIlap1plFDMg==", - "dependencies": { - "tslib": "~2.3.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@graphql-tools/wrap": { - "version": "8.4.11", - "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-8.4.11.tgz", - "integrity": "sha512-bif9yNZCoG1fFTGuIV4UblsJI95VSufl0RReXdr6f2yNbnqjSzgoDMB17WQlLrNOBrXa7r8N5aWBr5hBGhtGig==", - "dev": true, - "dependencies": { - "@graphql-tools/delegate": "8.7.2", - "@graphql-tools/schema": "8.3.7", - "@graphql-tools/utils": "8.6.6", - "tslib": "~2.3.0", - "value-or-promise": "1.0.11" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/@hubspot/api-client": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@hubspot/api-client/-/api-client-7.1.2.tgz", - "integrity": "sha512-JVQqh0fdHf97ePk0Hg/7BJsiXNlS9HQRPiM/CLgvVWt5CIviSLQ/kHLZXREmZqTWu7BisjCgHxnSx/d7gRdr2g==", - "dependencies": { - "bluebird": "^3.7.2", - "bottleneck": "^2.19.5", - "btoa": "^1.2.1", - "es6-promise": "^4.2.4", - "form-data": "^2.5.0", - "lodash": "^4.17.21", - "node-fetch": "^2.6.0", - "url-parse": "^1.4.3" - } - }, - "node_modules/@hubspot/api-client/node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", - "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "dev": true - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@juggle/resize-observer": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz", - "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" - }, - "node_modules/@n1ru4l/graphql-live-query": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz", - "integrity": "sha512-BTpWy1e+FxN82RnLz4x1+JcEewVdfmUhV1C6/XYD5AjS7PQp9QFF7K8bCD6gzPTr2l+prvqOyVueQhFJxB1vfg==", - "dev": true, - "peerDependencies": { - "graphql": "^15.4.0 || ^16.0.0" - } - }, - "node_modules/@next/bundle-analyzer": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-12.1.5.tgz", - "integrity": "sha512-A9MkhWCPvSp1vl0Ox7IjJ/qpugDC5YAb40btGGIPPXHQtkal107Sf8dbay4fqw4Hekee5gdS0WUMfe1BaSur7w==", - "dev": true, - "dependencies": { - "webpack-bundle-analyzer": "4.3.0" - } - }, - "node_modules/@next/env": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.2.5.tgz", - "integrity": "sha512-vLPLV3cpPGjUPT3PjgRj7e3nio9t6USkuew3JE/jMeon/9Mvp1WyR18v3iwnCuX7eUAm1HmAbJHHLAbcu/EJcw==" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.5.tgz", - "integrity": "sha512-Cnb8ERC5bNKBFrnMH6203sp/b0Y78QRx1XsFu+86oBtDBmQmOFoHu7teQjHm69ER73XKK3aGaeoLiXacHoUFsg==", - "dev": true, - "dependencies": { - "glob": "7.1.7" - } - }, - "node_modules/@next/eslint-plugin-next/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@next/eslint-plugin-next/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@next/swc-android-arm-eabi": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.5.tgz", - "integrity": "sha512-cPWClKxGhgn2dLWnspW+7psl3MoLQUcNqJqOHk2BhNcou9ARDtC0IjQkKe5qcn9qg7I7U83Gp1yh2aesZfZJMA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-android-arm64": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.2.5.tgz", - "integrity": "sha512-vMj0efliXmC5b7p+wfcQCX0AfU8IypjkzT64GiKJD9PgiA3IILNiGJr1fw2lyUDHkjeWx/5HMlMEpLnTsQslwg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.5.tgz", - "integrity": "sha512-VOPWbO5EFr6snla/WcxUKtvzGVShfs302TEMOtzYyWni6f9zuOetijJvVh9CCTzInnXAZMtHyNhefijA4HMYLg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.5.tgz", - "integrity": "sha512-5o8bTCgAmtYOgauO/Xd27vW52G2/m3i5PX7MUYePquxXAnX73AAtqA3WgPXBRitEB60plSKZgOTkcpqrsh546A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-freebsd-x64": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.5.tgz", - "integrity": "sha512-yYUbyup1JnznMtEBRkK4LT56N0lfK5qNTzr6/DEyDw5TbFVwnuy2hhLBzwCBkScFVjpFdfiC6SQAX3FrAZzuuw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.5.tgz", - "integrity": "sha512-2ZE2/G921Acks7UopJZVMgKLdm4vN4U0yuzvAMJ6KBavPzqESA2yHJlm85TV/K9gIjKhSk5BVtauIUntFRP8cg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.5.tgz", - "integrity": "sha512-/I6+PWVlz2wkTdWqhlSYYJ1pWWgUVva6SgX353oqTh8njNQp1SdFQuWDqk8LnM6ulheVfSsgkDzxrDaAQZnzjQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.5.tgz", - "integrity": "sha512-LPQRelfX6asXyVr59p5sTpx5l+0yh2Vjp/R8Wi4X9pnqcayqT4CUJLiHqCvZuLin3IsFdisJL0rKHMoaZLRfmg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.5.tgz", - "integrity": "sha512-0szyAo8jMCClkjNK0hknjhmAngUppoRekW6OAezbEYwHXN/VNtsXbfzgYOqjKWxEx3OoAzrT3jLwAF0HdX2MEw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.2.5.tgz", - "integrity": "sha512-zg/Y6oBar1yVnW6Il1I/08/2ukWtOG6s3acdJdEyIdsCzyQi4RLxbbhkD/EGQyhqBvd3QrC6ZXQEXighQUAZ0g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.5.tgz", - "integrity": "sha512-3/90DRNSqeeSRMMEhj4gHHQlLhhKg5SCCoYfE3kBjGpE63EfnblYUqsszGGZ9ekpKL/R4/SGB40iCQr8tR5Jiw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.5.tgz", - "integrity": "sha512-hGLc0ZRAwnaPL4ulwpp4D2RxmkHQLuI8CFOEEHdzZpS63/hMVzv81g8jzYA0UXbb9pus/iTc3VRbVbAM03SRrw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.5.tgz", - "integrity": "sha512-7h5/ahY7NeaO2xygqVrSG/Y8Vs4cdjxIjowTZ5W6CKoTKn7tmnuxlUc2h74x06FKmbhAd9agOjr/AOKyxYYm9Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@notionhq/client": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@notionhq/client/-/client-1.0.4.tgz", - "integrity": "sha512-m7zZ5l3RUktayf1lRBV1XMb8HSKsmWTv/LZPqP7UGC1NMzOlc+bbTOPNQ4CP/c1P4cP61VWLb/zBq7a3c0nMaw==", - "dependencies": { - "@types/node-fetch": "^2.5.10", - "node-fetch": "^2.6.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.21", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", - "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", - "dev": true - }, - "node_modules/@radix-ui/primitive": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-0.1.0.tgz", - "integrity": "sha512-tqxZKybwN5Fa3VzZry4G6mXAAb9aAqKmPtnVbZpL0vsBwvOHTBwsjHVPXylocYLwEtBY9SCe665bYnNB515uoA==", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/@radix-ui/react-navigation-menu": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-0.1.2.tgz", - "integrity": "sha512-b9+2ambunxMNw4pgOt9xqzVN/A5NPajpiKvR45zI/BS/uOWfV1A3N5Kn+OIbRMln9GyBnuoxXV0tUBLC2rAzpw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "0.1.0", - "@radix-ui/react-collection": "0.1.4", - "@radix-ui/react-compose-refs": "0.1.0", - "@radix-ui/react-context": "0.1.1", - "@radix-ui/react-dismissable-layer": "0.1.5", - "@radix-ui/react-id": "0.1.5", - "@radix-ui/react-presence": "0.1.2", - "@radix-ui/react-primitive": "0.1.4", - "@radix-ui/react-use-callback-ref": "0.1.0", - "@radix-ui/react-use-controllable-state": "0.1.0", - "@radix-ui/react-use-direction": "0.1.0", - "@radix-ui/react-use-layout-effect": "0.1.0", - "@radix-ui/react-use-previous": "0.1.1", - "@radix-ui/react-visually-hidden": "0.1.4" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0", - "react-dom": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-collection": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-0.1.4.tgz", - "integrity": "sha512-3muGI15IdgaDFjOcO7xX8a35HQRBRF6LH9pS6UCeZeRmbslkVeHyJRQr2rzICBUoX7zgIA0kXyMDbpQnJGyJTA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "0.1.0", - "@radix-ui/react-context": "0.1.1", - "@radix-ui/react-primitive": "0.1.4", - "@radix-ui/react-slot": "0.1.2" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-0.1.2.tgz", - "integrity": "sha512-ADkqfL+agEzEguU3yS26jfB50hRrwf7U4VTwAOZEmi/g+ITcBWe12yM46ueS/UCIMI9Py+gFUaAdxgxafFvY2Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "0.1.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-compose-refs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-0.1.0.tgz", - "integrity": "sha512-eyclbh+b77k+69Dk72q3694OHrn9B3QsoIRx7ywX341U9RK1ThgQjMFZoPtmZNQTksXHLNEiefR8hGVeFyInGg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-context": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-0.1.1.tgz", - "integrity": "sha512-PkyVX1JsLBioeu0jB9WvRpDBBLtLZohVDT3BB5CTSJqActma8S8030P57mWZb4baZifMvN7KKWPAA40UmWKkQg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-dismissable-layer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-0.1.5.tgz", - "integrity": "sha512-J+fYWijkX4M4QKwf9dtu1oC0U6e6CEl8WhBp3Ad23yz2Hia0XCo6Pk/mp5CAFy4QBtQedTSkhW05AdtSOEoajQ==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "0.1.0", - "@radix-ui/react-compose-refs": "0.1.0", - "@radix-ui/react-primitive": "0.1.4", - "@radix-ui/react-use-body-pointer-events": "0.1.1", - "@radix-ui/react-use-callback-ref": "0.1.0", - "@radix-ui/react-use-escape-keydown": "0.1.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-body-pointer-events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.1.1.tgz", - "integrity": "sha512-R8leV2AWmJokTmERM8cMXFHWSiv/fzOLhG/JLmRBhLTAzOj37EQizssq4oW0Z29VcZy2tODMi9Pk/htxwb+xpA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "0.1.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-escape-keydown": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-0.1.0.tgz", - "integrity": "sha512-tDLZbTGFmvXaazUXXv8kYbiCcbAE8yKgng9s95d8fCO+Eundv0Jngbn/hKPhDDs4jj9ChwRX5cDDnlaN+ugYYQ==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "0.1.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-id": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-0.1.5.tgz", - "integrity": "sha512-IPc4H/63bes0IZ1GJJozSEkSWcDyhNGtKFWUpJ+XtaLyQ1X3x7Mf6fWwWhDcpqlYEP+5WtAvfqcyEsyjP+ZhBQ==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "0.1.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-primitive": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz", - "integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "0.1.2" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-0.1.2.tgz", - "integrity": "sha512-ADkqfL+agEzEguU3yS26jfB50hRrwf7U4VTwAOZEmi/g+ITcBWe12yM46ueS/UCIMI9Py+gFUaAdxgxafFvY2Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "0.1.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-use-callback-ref": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-0.1.0.tgz", - "integrity": "sha512-Va041McOFFl+aV+sejvl0BS2aeHx86ND9X/rVFmEFQKTXCp6xgUK0NGUAGcgBlIjnJSbMYPGEk1xKSSlVcN2Aw==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-use-controllable-state": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-0.1.0.tgz", - "integrity": "sha512-zv7CX/PgsRl46a52Tl45TwqwVJdmqnlQEQhaYMz/yBOD2sx2gCkCFSoF/z9mpnYWmS6DTLNTg5lIps3fV6EnXg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "0.1.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-use-direction": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-direction/-/react-use-direction-0.1.0.tgz", - "integrity": "sha512-NajpY/An9TCPSfOVkgWIdXJV+VuWl67PxB6kOKYmtNAFHvObzIoh8o0n9sAuwSAyFCZVq211FEf9gvVDRhOyiA==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-use-layout-effect": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-0.1.0.tgz", - "integrity": "sha512-+wdeS51Y+E1q1Wmd+1xSSbesZkpVj4jsg0BojCbopWvgq5iBvixw5vgemscdh58ep98BwUbsFYnrywFhV9yrVg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-use-previous": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-0.1.1.tgz", - "integrity": "sha512-O/ZgrDBr11dR8rhO59ED8s5zIXBRFi8MiS+CmFGfi7MJYdLbfqVOmQU90Ghf87aifEgWe6380LA69KBneaShAg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-navigation-menu/node_modules/@radix-ui/react-visually-hidden": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-0.1.4.tgz", - "integrity": "sha512-K/q6AEEzqeeEq/T0NPChvBqnwlp8Tl4NnQdrI/y8IOY7BRR+Ug0PEsVk6g48HJ7cA1//COugdxXXVVK/m0X1mA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "0.1.4" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-polymorphic": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-polymorphic/-/react-polymorphic-0.0.14.tgz", - "integrity": "sha512-9nsMZEDU3LeIUeHJrpkkhZVxu/9Fc7P2g2I3WR+uA9mTbNC3hGaabi0dV6wg0CfHb+m4nSs1pejbE/5no3MJTA==", - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-0.1.2.tgz", - "integrity": "sha512-3BRlFZraooIUfRlyN+b/Xs5hq1lanOOo/+3h6Pwu2GMFjkGKKa4Rd51fcqGqnVlbr3jYg+WLuGyAV4KlgqwrQw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "0.1.0", - "@radix-ui/react-use-layout-effect": "0.1.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-compose-refs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-0.1.0.tgz", - "integrity": "sha512-eyclbh+b77k+69Dk72q3694OHrn9B3QsoIRx7ywX341U9RK1ThgQjMFZoPtmZNQTksXHLNEiefR8hGVeFyInGg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-use-layout-effect": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-0.1.0.tgz", - "integrity": "sha512-+wdeS51Y+E1q1Wmd+1xSSbesZkpVj4jsg0BojCbopWvgq5iBvixw5vgemscdh58ep98BwUbsFYnrywFhV9yrVg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/@reach/dialog": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@reach/dialog/-/dialog-0.17.0.tgz", - "integrity": "sha512-AnfKXugqDTGbeG3c8xDcrQDE4h9b/vnc27Sa118oQSquz52fneUeX9MeFb5ZEiBJK8T5NJpv7QUTBIKnFCAH5A==", - "dependencies": { - "@reach/portal": "0.17.0", - "@reach/utils": "0.17.0", - "prop-types": "^15.7.2", - "react-focus-lock": "^2.5.2", - "react-remove-scroll": "^2.4.3", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "react": "^16.8.0 || 17.x", - "react-dom": "^16.8.0 || 17.x" - } - }, - "node_modules/@reach/dialog/node_modules/@reach/portal": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@reach/portal/-/portal-0.17.0.tgz", - "integrity": "sha512-+IxsgVycOj+WOeNPL2NdgooUdHPSY285wCtj/iWID6akyr4FgGUK7sMhRM9aGFyrGpx2vzr+eggbUmAVZwOz+A==", - "dependencies": { - "@reach/utils": "0.17.0", - "tiny-warning": "^1.0.3", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "react": "^16.8.0 || 17.x", - "react-dom": "^16.8.0 || 17.x" - } - }, - "node_modules/@reach/dialog/node_modules/@reach/utils": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.17.0.tgz", - "integrity": "sha512-M5y8fCBbrWeIsxedgcSw6oDlAMQDkl5uv3VnMVJ7guwpf4E48Xlh1v66z/1BgN/WYe2y8mB/ilFD2nysEfdGeA==", - "dependencies": { - "tiny-warning": "^1.0.3", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "react": "^16.8.0 || 17.x", - "react-dom": "^16.8.0 || 17.x" - } - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.8.tgz", - "integrity": "sha512-ZK5v4bJwgXldAUA8r3q9YKfCwOqoHTK/ZqRjSeRXQrBXWouoPnS4MQtgC4AXGiiBuUu5wxrRgTlv0ktmM4P1Aw==", - "dev": true - }, - "node_modules/@samverschueren/stream-to-observable": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", - "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", - "dev": true, - "dependencies": { - "any-observable": "^0.3.0" - }, - "engines": { - "node": ">=6" - }, - "peerDependenciesMeta": { - "rxjs": { - "optional": true - }, - "zen-observable": { - "optional": true - } - } - }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@swc/helpers": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.3.tgz", - "integrity": "sha512-6JrF+fdUK2zbGpJIlN7G3v966PQjyx/dPt1T9km2wj+EUBqgrxCk3uX4Kct16MIm9gGxfKRcfax2hVf5jvlTzA==", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@swc/helpers/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@types/css-font-loading-module": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", - "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==", - "dev": true - }, - "node_modules/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/json-stable-stringify": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz", - "integrity": "sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4= sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/jsonwebtoken": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz", - "integrity": "sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/lodash": { - "version": "4.14.182", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", - "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==" - }, - "node_modules/@types/marked": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.3.tgz", - "integrity": "sha512-HnMWQkLJEf/PnxZIfbm0yGJRRZYYMhb++O9M36UCTA9z53uPvVoSlAwJr3XOpDEryb7Hwl1qAx/MV6YIW1RXxg==" - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "17.0.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", - "integrity": "sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==" - }, - "node_modules/@types/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", - "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.4", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", - "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==" - }, - "node_modules/@types/react": { - "version": "17.0.44", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.44.tgz", - "integrity": "sha512-Ye0nlw09GeMp2Suh8qoOv0odfgCoowfM/9MG6WeRD60Gq9wS90bdkdRtYbRkNhXOpG4H+YXGvj4wOWhAC0LJ1g==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "17.0.15", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.15.tgz", - "integrity": "sha512-Tr9VU9DvNoHDWlmecmcsE5ZZiUkYx+nKBzum4Oxe1K0yJVyBlfbq7H3eXjxXqJczBKqPGq3EgfTru4MgKb9+Yw==", - "dev": true, - "dependencies": { - "@types/react": "^17" - } - }, - "node_modules/@types/react-google-recaptcha": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/react-google-recaptcha/-/react-google-recaptcha-2.1.5.tgz", - "integrity": "sha512-iWTjmVttlNgp0teyh7eBXqNOQzVq2RWNiFROWjraOptRnb1OcHJehQnji0sjqIRAk9K0z8stjyhU+OLpPb0N6w==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", - "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "node_modules/@types/tiny-json-http": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@types/tiny-json-http/-/tiny-json-http-7.3.1.tgz", - "integrity": "sha512-mJwIwN1W9VLVM7FrGPuWabuEH5FqcOGZByqV4qOSNiwMKtEKym5+6Dyhx2v7vbR/4rSjjUgngmYimaDHQbZQsw==", - "dev": true - }, - "node_modules/@types/websocket": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.5.tgz", - "integrity": "sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.20.0.tgz", - "integrity": "sha512-fapGzoxilCn3sBtC6NtXZX6+P/Hef7VDbyfGqTTpzYydwhlkevB+0vE0EnmHPVTVSy68GUncyJ/2PcrFBeCo5Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/type-utils": "5.20.0", - "@typescript-eslint/utils": "5.20.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.20.0.tgz", - "integrity": "sha512-UWKibrCZQCYvobmu3/N8TWbEeo/EPQbS41Ux1F9XqPzGuV7pfg6n50ZrFo6hryynD8qOTTfLHtHjjdQtxJ0h/w==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/typescript-estree": "5.20.0", - "debug": "^4.3.2" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.20.0.tgz", - "integrity": "sha512-h9KtuPZ4D/JuX7rpp1iKg3zOH0WNEa+ZIXwpW/KWmEFDxlA/HSfCMhiyF1HS/drTICjIbpA6OqkAhrP/zkCStg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/visitor-keys": "5.20.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.20.0.tgz", - "integrity": "sha512-WxNrCwYB3N/m8ceyoGCgbLmuZwupvzN0rE8NBuwnl7APgjv24ZJIjkNzoFBXPRCGzLNkoU/WfanW0exvp/+3Iw==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.20.0", - "debug": "^4.3.2", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz", - "integrity": "sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.20.0.tgz", - "integrity": "sha512-36xLjP/+bXusLMrT9fMMYy1KJAGgHhlER2TqpUVDYUQg4w0q/NW/sg4UGAgVwAqb8V4zYg43KMUpM8vV2lve6w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/visitor-keys": "5.20.0", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.20.0.tgz", - "integrity": "sha512-lHONGJL1LIO12Ujyx8L8xKbwWSkoUKFSO+0wDAqGXiudWB2EO7WEUT+YZLtVbmOmSllAjLb9tpoIPwpRe5Tn6w==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.20.0", - "@typescript-eslint/types": "5.20.0", - "@typescript-eslint/typescript-estree": "5.20.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.20.0.tgz", - "integrity": "sha512-1flRpNF+0CAQkMNlTJ6L/Z5jiODG/e5+7mk6XwtPOUS3UrTz3UOiAg9jG2VtKsWI6rZQfy4C6a232QNRZTRGlg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.20.0", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/aggregate-error/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "devOptional": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "node_modules/are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/are-we-there-yet/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/are-we-there-yet/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/array-flatten": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", - "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==" - }, - "node_modules/array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", - "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", - "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0= sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", - "dev": true - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/auto-bind": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", - "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/axe-core": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz", - "integrity": "sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-macros": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", - "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", - "dependencies": { - "@babel/runtime": "^7.7.2", - "cosmiconfig": "^6.0.0", - "resolve": "^1.12.0" - } - }, - "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-syntax-trailing-function-commas": { - "version": "7.0.0-beta.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", - "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==" - }, - "node_modules/babel-preset-fbjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", - "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==", - "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-class-properties": "^7.0.0", - "@babel/plugin-syntax-flow": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "@babel/plugin-transform-arrow-functions": "^7.0.0", - "@babel/plugin-transform-block-scoped-functions": "^7.0.0", - "@babel/plugin-transform-block-scoping": "^7.0.0", - "@babel/plugin-transform-classes": "^7.0.0", - "@babel/plugin-transform-computed-properties": "^7.0.0", - "@babel/plugin-transform-destructuring": "^7.0.0", - "@babel/plugin-transform-flow-strip-types": "^7.0.0", - "@babel/plugin-transform-for-of": "^7.0.0", - "@babel/plugin-transform-function-name": "^7.0.0", - "@babel/plugin-transform-literals": "^7.0.0", - "@babel/plugin-transform-member-expression-literals": "^7.0.0", - "@babel/plugin-transform-modules-commonjs": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.0.0", - "@babel/plugin-transform-parameters": "^7.0.0", - "@babel/plugin-transform-property-literals": "^7.0.0", - "@babel/plugin-transform-react-display-name": "^7.0.0", - "@babel/plugin-transform-react-jsx": "^7.0.0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0", - "@babel/plugin-transform-spread": "^7.0.0", - "@babel/plugin-transform-template-literals": "^7.0.0", - "babel-plugin-syntax-trailing-function-commas": "^7.0.0-beta.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc= sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "devOptional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "devOptional": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/broadcast-channel": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", - "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", - "dependencies": { - "@babel/runtime": "^7.7.2", - "detect-node": "^2.1.0", - "js-sha3": "0.8.0", - "microseconds": "0.2.0", - "nano-time": "1.0.0", - "oblivious-set": "1.0.0", - "rimraf": "3.0.2", - "unload": "2.2.0" - } - }, - "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "bin": { - "btoa": "bin/btoa.js" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "dev": true - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001383", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001383.tgz", - "integrity": "sha512-swMpEoTp5vDoGBZsYZX7L7nXHe6dsHxi9o6/LKf/f0LukVtnrxly5GVb/fWdCDTqi/yw6Km6tiJ0pmBacm0gbg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/capital-case": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", - "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/change-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", - "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dependencies": { - "camel-case": "^4.1.2", - "capital-case": "^1.0.4", - "constant-case": "^3.0.4", - "dot-case": "^3.0.4", - "header-case": "^2.0.4", - "no-case": "^3.0.4", - "param-case": "^3.0.4", - "pascal-case": "^3.1.2", - "path-case": "^3.0.4", - "sentence-case": "^3.0.4", - "snake-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/change-case-all": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz", - "integrity": "sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA==", - "dependencies": { - "change-case": "^4.1.2", - "is-lower-case": "^2.0.2", - "is-upper-case": "^2.0.2", - "lower-case": "^2.0.2", - "lower-case-first": "^2.0.2", - "sponge-case": "^1.0.1", - "swap-case": "^2.0.2", - "title-case": "^3.0.3", - "upper-case": "^2.0.2", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4= sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-regexp": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", - "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", - "dev": true, - "dependencies": { - "is-regexp": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/clsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", - "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/constant-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", - "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case": "^2.0.2" - } - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/convert-source-map/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/core-js-pure": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", - "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==", - "deprecated": "core-js-pure@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js-pure.", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cosmiconfig-toml-loader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-toml-loader/-/cosmiconfig-toml-loader-1.0.0.tgz", - "integrity": "sha512-H/2gurFWVi7xXvCyvsWRLCMekl4tITJcX0QEsDMpzxtuxDyM59xLatYNg4s/k9AA/HdtCYfj2su8mgA0GSDLDA==", - "dev": true, - "dependencies": { - "@iarna/toml": "^2.2.5" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", - "dependencies": { - "node-fetch": "2.6.7" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-undici-fetch": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/cross-undici-fetch/-/cross-undici-fetch-0.1.28.tgz", - "integrity": "sha512-/nLMyVE5IC9PQdBtmgjpGZfK0wo8UupomAPx+7HlbEgVDkZOa9xCiZP9goo5aLYofP0gHXgovjXdXrE2obANag==", - "dev": true, - "dependencies": { - "abort-controller": "^3.0.0", - "form-data-encoder": "^1.7.1", - "formdata-node": "^4.3.1", - "node-fetch": "^2.6.7", - "undici": "^5.0.0", - "web-streams-polyfill": "^3.2.0" - } - }, - "node_modules/css-functions-list": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", - "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", - "dev": true, - "engines": { - "node": ">=12.22" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", - "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true - }, - "node_modules/dataloader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz", - "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==", - "dev": true - }, - "node_modules/date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, - "node_modules/datocms-listen": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/datocms-listen/-/datocms-listen-0.1.8.tgz", - "integrity": "sha512-oyf7vBPTdqRyf0OTgK8SUmmv7FGgqacJeX4KqP4VhJ8kBrToog7aMRudwdA+iHFS8fuOeiDDH/SMg0PPqNP3JQ==" - }, - "node_modules/datocms-structured-text-generic-html-renderer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/datocms-structured-text-generic-html-renderer/-/datocms-structured-text-generic-html-renderer-2.0.4.tgz", - "integrity": "sha512-Rv5Y1eC9NZXNSz3FHvHGFvGnKEEbvG1kQWlylpCSoeDBZPBwSWAFD/ixIAOfa8Yk1EVpmoQd3KQd725WKYJwRQ==", - "dependencies": { - "datocms-structured-text-utils": "^2.0.4" - } - }, - "node_modules/datocms-structured-text-to-html-string": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/datocms-structured-text-to-html-string/-/datocms-structured-text-to-html-string-2.0.4.tgz", - "integrity": "sha512-ulet6wg3cxY4e4Edh0ilMfTfBK8VHeYZwhusx/fJSyk/3A1+PGiQ9EU2j2+OkOxvq+ePo0Iyzi7hgMEutSPRQQ==", - "dependencies": { - "datocms-structured-text-generic-html-renderer": "^2.0.4", - "datocms-structured-text-utils": "^2.0.4", - "vhtml": "^2.2.0" - } - }, - "node_modules/datocms-structured-text-to-plain-text": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/datocms-structured-text-to-plain-text/-/datocms-structured-text-to-plain-text-2.0.4.tgz", - "integrity": "sha512-/0VQNo6xZXiGdnHCTIoEmdnwD1JZVMMsyEfSy2SNF7QW4YAOz8bKtWSwCT4cYRKrhA/jZWCq7G4u0kPmiy1FWg==", - "dependencies": { - "datocms-structured-text-generic-html-renderer": "^2.0.4", - "datocms-structured-text-utils": "^2.0.4" - } - }, - "node_modules/datocms-structured-text-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/datocms-structured-text-utils/-/datocms-structured-text-utils-2.0.4.tgz", - "integrity": "sha512-JxDdJaipm3FePOli9s82MRrBDnlPP9GYhqajQJaYEu4V1jwUSZzbVkYVKMfVHUfP0Gh0VSCxPHjAGGAN1nWMJg==", - "dependencies": { - "array-flatten": "^3.0.0" - } - }, - "node_modules/debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "node_modules/dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/dequal": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", - "integrity": "sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dotenv": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", - "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/dset": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.1.tgz", - "integrity": "sha512-hYf+jZNNqJBD2GiMYb+5mqOIX4R4RRHXU3qWMWYN+rqcR2/YpRL2bUHr8C8fU+5DNvqYjJ8YvMGSLuVPWU1cNg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA==", - "dev": true - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.230", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.230.tgz", - "integrity": "sha512-3pwjAK0qHSDN9+YAF4fJknsSruP7mpjdWzUSruIJD/JCH77pEh0SorEyb3xVaKkfwk2tzjOt2D8scJ0KAdfXLA==" - }, - "node_modules/elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", - "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", - "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.2.1", - "@humanwhocodes/config-array": "^0.9.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-next": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-12.1.5.tgz", - "integrity": "sha512-P+DCt5ti63KhC0qNLzrAmPcwRGq8pYqgcf/NNr1E+WjCrMkWdCAXkIANTquo+kcO1adR2k1lTo5GCrNUtKy4hQ==", - "dev": true, - "dependencies": { - "@next/eslint-plugin-next": "12.1.5", - "@rushstack/eslint-patch": "1.0.8", - "@typescript-eslint/parser": "5.10.1", - "eslint-import-resolver-node": "0.3.4", - "eslint-import-resolver-typescript": "2.4.0", - "eslint-plugin-import": "2.25.2", - "eslint-plugin-jsx-a11y": "6.5.1", - "eslint-plugin-react": "7.29.1", - "eslint-plugin-react-hooks": "4.3.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0", - "next": ">=10.2.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-config-next/node_modules/@typescript-eslint/parser": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.10.1.tgz", - "integrity": "sha512-GReo3tjNBwR5RnRO0K2wDIDN31cM3MmDtgyQ85oAxAmC5K3j/g85IjP+cDfcqDsDDBf1HNKQAD0WqOYL8jXqUA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.10.1", - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/typescript-estree": "5.10.1", - "debug": "^4.3.2" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-config-next/node_modules/@typescript-eslint/scope-manager": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz", - "integrity": "sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/visitor-keys": "5.10.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-config-next/node_modules/@typescript-eslint/types": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.1.tgz", - "integrity": "sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-config-next/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz", - "integrity": "sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.10.1", - "@typescript-eslint/visitor-keys": "5.10.1", - "debug": "^4.3.2", - "globby": "^11.0.4", - "is-glob": "^4.0.3", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-config-next/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz", - "integrity": "sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.10.1", - "eslint-visitor-keys": "^3.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-config-next/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-import-resolver-typescript": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz", - "integrity": "sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-import": { - "version": "2.25.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.2.tgz", - "integrity": "sha512-qCwQr9TYfoBHOFcVGKY9C9unq05uOxxdklmBXLVvcwo68y5Hta6/GzCZEMx2zQiu0woKNEER0LE7ZgaOfBU14g==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.0", - "has": "^1.0.3", - "is-core-module": "^2.7.0", - "is-glob": "^4.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.11.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-react": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.1.tgz", - "integrity": "sha512-WtzRpHMhsOX05ZrkyaaqmLl2uXGqmYooCfBxftJKlkYdsltiufGgfU7uuoHwR2lBam2Kh/EIVID4aU9e3kbCMA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flatmap": "^1.2.5", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.0", - "object.values": "^1.1.5", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-react-hooks": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", - "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-config-next/node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-config-next/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-config-next/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", - "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "glob": "^7.2.0", - "is-glob": "^4.0.3", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", - "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz", - "integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.16.3", - "aria-query": "^4.2.2", - "array-includes": "^3.1.4", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.3.5", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.7", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.2.1", - "language-tags": "^1.0.5", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.29.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", - "integrity": "sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flatmap": "^1.2.5", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.0", - "object.values": "^1.1.5", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz", - "integrity": "sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-simple-import-sort": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz", - "integrity": "sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==", - "dev": true, - "peerDependencies": { - "eslint": ">=5.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", - "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", - "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", - "dev": true, - "dependencies": { - "acorn": "^8.7.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "dev": true - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", - "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", - "dev": true, - "dependencies": { - "clone-regexp": "^2.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extract-files": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz", - "integrity": "sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==", - "dev": true, - "engines": { - "node": "^12.20 || >= 14.13" - }, - "funding": { - "url": "https://github.com/sponsors/jaydenseric" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", - "dev": true, - "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.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fbjs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", - "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", - "dependencies": { - "cross-fetch": "^3.1.5", - "fbjs-css-vars": "^1.0.0", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.30" - } - }, - "node_modules/fbjs-css-vars": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" - }, - "node_modules/fbjs/node_modules/ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "devOptional": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c= sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "node_modules/focus-lock": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.11.2.tgz", - "integrity": "sha512-pZ2bO++NWLHhiKkgP1bEXHhR1/OjVcSvlCJ98aNJDFeb7H5OOQaO+SKOZle6041O9rv2tmbrO4JzClAvDUHf0g==", - "dependencies": { - "tslib": "^2.0.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "dev": true - }, - "node_modules/formdata-node": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.3.2.tgz", - "integrity": "sha512-k7lYJyzDOSL6h917favP8j1L0/wNyylzU+x+1w4p5haGVHNlP58dbpdJhiCUsDbWsa9HwEtLp89obQgXl2e0qg==", - "dev": true, - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.1" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/formdata-node/node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz", - "integrity": "sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8= sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8= sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs= sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globjoin": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM= sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", - "dev": true - }, - "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/graphql": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.3.0.tgz", - "integrity": "sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==", - "engines": { - "node": "^12.22.0 || ^14.16.0 || >=16.0.0" - } - }, - "node_modules/graphql-config": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-4.3.0.tgz", - "integrity": "sha512-Uiu3X7+s5c056WyrvdZVz2vG1fhAipMlYmtiCU/4Z2mX79OXDr1SqIon2MprC/pExIWJfAQZCcjYDY76fPBUQg==", - "dev": true, - "dependencies": { - "@endemolshinegroup/cosmiconfig-typescript-loader": "3.0.2", - "@graphql-tools/graphql-file-loader": "^7.3.7", - "@graphql-tools/json-file-loader": "^7.3.7", - "@graphql-tools/load": "^7.5.5", - "@graphql-tools/merge": "^8.2.6", - "@graphql-tools/url-loader": "^7.9.7", - "@graphql-tools/utils": "^8.6.5", - "cosmiconfig": "7.0.1", - "cosmiconfig-toml-loader": "1.0.0", - "minimatch": "4.2.1", - "string-env-interpolation": "1.0.1" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/graphql-executor": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/graphql-executor/-/graphql-executor-0.0.22.tgz", - "integrity": "sha512-WbKSnSHFn6REKKH4T6UAwDM3mLUnYMQlQLNG0Fw+Lkb3ilCnL3m5lkJ7411LAI9sF7BvPbthovVZhsEUh9Xfag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.16.0 || >=16.0.0" - }, - "peerDependencies": { - "graphql": "^15.0.0 || ^16.0.0" - } - }, - "node_modules/graphql-request": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-4.2.0.tgz", - "integrity": "sha512-uFeMyhhl8ss4LFgjlfPeAn2pqYw+CJto+cjj71uaBYIMMK2jPIqgHm5KEFxUk0YDD41A8Bq31a2b4G2WJBlp2Q==", - "dependencies": { - "cross-fetch": "^3.1.5", - "extract-files": "^9.0.0", - "form-data": "^3.0.0" - }, - "peerDependencies": { - "graphql": "14 - 16" - } - }, - "node_modules/graphql-request/node_modules/extract-files": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", - "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==", - "engines": { - "node": "^10.17.0 || ^12.0.0 || >= 13.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/jaydenseric" - } - }, - "node_modules/graphql-sse": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/graphql-sse/-/graphql-sse-1.1.0.tgz", - "integrity": "sha512-xE8AGPJa5X+g7iFmRQw/8H+7lXIDJvSkW6lou/XSSq17opPQl+dbKOMiqraHMx52VrDgS061ZVx90OSuqS6ykA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "graphql": ">=0.11 <=16" - } - }, - "node_modules/graphql-tag": { - "version": "2.12.6", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", - "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } - }, - "node_modules/graphql-ws": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.7.0.tgz", - "integrity": "sha512-8yYuvnyqIjlJ/WfebOyu2GSOQeFauRxnfuTveY9yvrDGs2g3kR9Nv4gu40AKvRHbXlSJwTbMJ6dVxAtEyKwVRA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "graphql": ">=0.11 <=16" - } - }, - "node_modules/gsap": { - "version": "3.10.2", - "resolved": "file:src/lib/gsap/gsap-bonus.tgz", - "integrity": "sha512-wKrHH2HLKCu/cmWqFCEoHE2fwex4YbLxEdeFuXUITRF+I+6NxLnFhiMVkK0OSPWE9uI6/YBoXURnHlncyTbSQQ==", - "license": "This package should only be used by individuals/companies with an active Club GreenSock membership. See https://greensock.com/club/. Licensing: https://greensock.com/licensing/" - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dev": true, - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8= sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "node_modules/header-case": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", - "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dependencies": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/immutable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", - "devOptional": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", - "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", - "engines": { - "node": ">=12.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o= sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/inquirer": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz", - "integrity": "sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz", - "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "dev": true, - "dependencies": { - "symbol-observable": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4= sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regexp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", - "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", - "dependencies": { - "is-unc-path": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", - "dependencies": { - "unc-path-regex": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", - "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "dev": true, - "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, - "node_modules/isomorphic-ws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", - "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", - "dev": true, - "peerDependencies": { - "ws": "*" - } - }, - "node_modules/iterall": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", - "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", - "dev": true - }, - "node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= sha512-i/J297TW6xyj7sDFa7AmBPkQvLIxWr2kKPWI26tXydnZrzVAocNqn5DMNT1Mzk0vit1V5UkRM7C1KdVNp7Lmcg==", - "dev": true, - "dependencies": { - "jsonify": "~0.0.0" - } - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json-to-pretty-yaml": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", - "integrity": "sha1-9M0L0KXo/h3yWq9boRiwmf2ZLVs= sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==", - "dev": true, - "dependencies": { - "remedial": "^1.0.7", - "remove-trailing-spaces": "^1.0.6" - }, - "engines": { - "node": ">= 0.2.0" - } - }, - "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= sha512-trvBk1ki43VZptdBI5rIlG4YOzyeH/WefQt5rj1grasPn4iiZWKet8nkgc4GlsAylaztn0qZfUYOiTsASJFdNA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/jsonp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", - "integrity": "sha1-pltPoPEL2nGaBUQep7lMVfPhW64= sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", - "dependencies": { - "debug": "^2.1.3" - } - }, - "node_modules/jsonp/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/jsonp/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "dev": true, - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=4", - "npm": ">=1.4.28" - } - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.2.tgz", - "integrity": "sha512-HDAyJ4MNQBboGpUnHAVUNJs6X0lh058s6FuixsFGP7MgJYpD6Vasd6nzSG5iIfXu1zAYlHJ/zsOKNlrenTUBnw==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.4", - "object.assign": "^4.1.2" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dev": true, - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dev": true, - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keen-slider": { - "version": "6.6.9", - "resolved": "https://registry.npmjs.org/keen-slider/-/keen-slider-6.6.9.tgz", - "integrity": "sha512-3XjWVWAL2NDLx9CT9vIsoEAY2vP18bHwsiB9X49QAuNiV5ljAZLPqx5eiZz8bKhmnv1Jc3UUpTzRTDRPeDwx2g==" - }, - "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/known-css-properties": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.24.0.tgz", - "integrity": "sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA==", - "dev": true - }, - "node_modules/language-subtag-registry": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", - "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", - "dev": true - }, - "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", - "dev": true, - "dependencies": { - "language-subtag-registry": "~0.3.2" - } - }, - "node_modules/latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "dependencies": { - "package-json": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/lint-staged": { - "version": "12.3.8", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.3.8.tgz", - "integrity": "sha512-0+UpNaqIwKRSGAFOCcpuYNIv/j5QGVC+xUVvmSdxHO+IfIGoHbFLo3XcPmV/LLnsVj5EAncNHVtlITSoY5qWGQ==", - "dev": true, - "dependencies": { - "cli-truncate": "^3.1.0", - "colorette": "^2.0.16", - "commander": "^8.3.0", - "debug": "^4.3.3", - "execa": "^5.1.1", - "lilconfig": "2.0.4", - "listr2": "^4.0.1", - "micromatch": "^4.0.4", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.0", - "pidtree": "^0.5.0", - "string-argv": "^0.3.1", - "supports-color": "^9.2.1", - "yaml": "^1.10.2" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/lint-staged/node_modules/supports-color": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz", - "integrity": "sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "dev": true, - "dependencies": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= sha512-L26cIFm7/oZeSNVhWB6faeorXhMg4HNlb/dS/7jHhr708jxlXrtrBWo4YUxZQkc6dGoxEAe6J/D3juTRBUzjtA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", - "dev": true, - "dependencies": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "listr": "^0.14.2" - } - }, - "node_modules/listr-update-renderer/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8= sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg==", - "dev": true, - "dependencies": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/listr-update-renderer/node_modules/figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs= sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= sha512-mmPrW0Fh2fxOzdBbFv4g1m6pR72haFLPJ2G5SJEELf1y+iaQrDG6cWCPjy54RHYbZAt7X+ls690Kw62AdWXBzQ==", - "dev": true, - "dependencies": { - "chalk": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr-update-renderer/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", - "dev": true, - "dependencies": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dev": true, - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/listr-verbose-renderer/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/listr-verbose-renderer/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/listr-verbose-renderer/node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0= sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368= sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "dev": true, - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr-verbose-renderer/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/listr/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ= sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/listr/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/listr/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", - "dev": true, - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.5", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/listr2/node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/load-script": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", - "integrity": "sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ= sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==" - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "dev": true - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "dev": true - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "dev": true - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "dev": true - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg= sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", - "dev": true, - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368= sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", - "dev": true, - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8= sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", - "dev": true, - "dependencies": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lower-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz", - "integrity": "sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/marked": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", - "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/match-sorter": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", - "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "remove-accents": "0.4.2" - } - }, - "node_modules/mathml-tag-names": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", - "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" - }, - "node_modules/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/meros": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/meros/-/meros-1.2.0.tgz", - "integrity": "sha512-3QRZIS707pZQnijHdhbttXRWwrHhZJ/gzolneoxKVz9N/xmsvY/7Ls8lpnI9gxbgxjcHsAVEW3mgwiZCo6kkJQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "@types/node": ">=12" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/microseconds": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", - "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, - "node_modules/mrmime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.0.tgz", - "integrity": "sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/nano-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", - "integrity": "sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8= sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", - "dependencies": { - "big-integer": "^1.6.16" - } - }, - "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/next": { - "version": "12.2.5", - "resolved": "https://registry.npmjs.org/next/-/next-12.2.5.tgz", - "integrity": "sha512-tBdjqX5XC/oFs/6gxrZhjmiq90YWizUYU6qOWAfat7zJwrwapJ+BYgX2PmiacunXMaRpeVT4vz5MSPSLgNkrpA==", - "dependencies": { - "@next/env": "12.2.5", - "@swc/helpers": "0.4.3", - "caniuse-lite": "^1.0.30001332", - "postcss": "8.4.14", - "styled-jsx": "5.0.4", - "use-sync-external-store": "1.2.0" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=12.22.0" - }, - "optionalDependencies": { - "@next/swc-android-arm-eabi": "12.2.5", - "@next/swc-android-arm64": "12.2.5", - "@next/swc-darwin-arm64": "12.2.5", - "@next/swc-darwin-x64": "12.2.5", - "@next/swc-freebsd-x64": "12.2.5", - "@next/swc-linux-arm-gnueabihf": "12.2.5", - "@next/swc-linux-arm64-gnu": "12.2.5", - "@next/swc-linux-arm64-musl": "12.2.5", - "@next/swc-linux-x64-gnu": "12.2.5", - "@next/swc-linux-x64-musl": "12.2.5", - "@next/swc-win32-arm64-msvc": "12.2.5", - "@next/swc-win32-ia32-msvc": "12.2.5", - "@next/swc-win32-x64-msvc": "12.2.5" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^6.0.0 || ^7.0.0", - "react": "^17.0.2 || ^18.0.0-0", - "react-dom": "^17.0.2 || ^18.0.0-0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next-compose-plugins": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/next-compose-plugins/-/next-compose-plugins-2.2.1.tgz", - "integrity": "sha512-OjJ+fV15FXO2uQXQagLD4C0abYErBjyjE0I0FHpOEIB8upw0hg1ldFP6cqHTJBH1cZqy96OeR3u1dJ+Ez2D4Bg==", - "dev": true - }, - "node_modules/next-real-viewport": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/next-real-viewport/-/next-real-viewport-0.7.0.tgz", - "integrity": "sha512-Hs9+J+S4y1CaJ+t7v46/G54MuUYPdUyyctOrYUcGd299FFHSDjq4VCjhtc9vcY4LE+OpnY4BXZ/+ENUSJ/JFQg==", - "peerDependencies": { - "next": "^12.1.0", - "react": "*", - "react-dom": "*" - } - }, - "node_modules/next-seo": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/next-seo/-/next-seo-5.4.0.tgz", - "integrity": "sha512-R9DhajPwJnR/lsF2hZ8cN8uqr5CVITsRrCG1AF5+ufcaybKYOvnH8sH9MaH4/hpkps3PQ9H71S7J7SPYixAYzQ==", - "peerDependencies": { - "next": "^8.1.1-canary.54 || >=9.0.0", - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/next-sitemap": { - "version": "2.5.20", - "resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-2.5.20.tgz", - "integrity": "sha512-Qbm4N2WGA6VHemFN0C9PNWQav9RKwMEbVrNgVvDQhG0sDBmNBgKii54WklOjCvVJVHgQPgtXLBhlNaJGS+RVQA==", - "dev": true, - "dependencies": { - "@corex/deepmerge": "^2.6.148", - "minimist": "^1.2.6" - }, - "bin": { - "next-sitemap": "bin/next-sitemap" - }, - "engines": { - "node": ">=14.18" - }, - "peerDependencies": { - "next": "*" - } - }, - "node_modules/next-themes": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.0.tgz", - "integrity": "sha512-myhpDL4vadBD9YDSHiewqvzorGzB03N84e+3LxCwHRlM/hiBOaW+UsKsQojQAzC7fdcJA0l2ppveXcYaVV+hxQ==", - "peerDependencies": { - "next": "*", - "react": "*", - "react-dom": "*" - } - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-abi": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.8.0.tgz", - "integrity": "sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw==", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" - }, - "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-selector": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", - "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= sha512-dxvWdI8gw6eAvk9BlPffgEoGfM7AdijoCwOEJge3e3ulT2XLgmU7KvvxprOaCu05Q1uGRHmOhHe1r6emZoKyFw==", - "dev": true - }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/oblivious-set": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", - "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true, - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "dependencies": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", - "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", - "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18= sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", - "dependencies": { - "path-root-regex": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", - "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", - "dev": true - }, - "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4= sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", - "dev": true - }, - "node_modules/postcss-safe-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", - "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", - "dev": true, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-scss": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.3.tgz", - "integrity": "sha512-j4KxzWovfdHsyxwl1BxkUal/O4uirvHgdzMKS1aWJBAV0qh2qj5qAZqpeBfVUYGWv+4iK9Az7SPyZ4fyNju1uA==", - "dev": true, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/prebuild-install": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.0.1.tgz", - "integrity": "sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg==", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/prettier": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", - "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo= sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-async-script": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", - "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", - "dependencies": { - "hoist-non-react-statics": "^3.3.0", - "prop-types": "^15.5.0" - }, - "peerDependencies": { - "react": ">=16.4.1" - } - }, - "node_modules/react-clientside-effect": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz", - "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==", - "dependencies": { - "@babel/runtime": "^7.12.13" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-datocms": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/react-datocms/-/react-datocms-3.0.12.tgz", - "integrity": "sha512-GbIHncVmgYok5eYAP2ChhyJ+29jzNTK+Qlp6RaVW4waDfJ3bqzTpYBB5jJsNHbIrEVuXUiblrTLKITnp/eyJbw==", - "dependencies": { - "datocms-listen": "^0.1.7", - "datocms-structured-text-generic-html-renderer": "^2.0.1", - "datocms-structured-text-utils": "^2.0.1", - "react-intersection-observer": "^8.33.1", - "universal-base64": "^2.1.0", - "use-deep-compare-effect": "^1.6.1" - }, - "peerDependencies": { - "react": ">= 16.12.0" - } - }, - "node_modules/react-device-detect": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-2.2.2.tgz", - "integrity": "sha512-zSN1gIAztUekp5qUT/ybHwQ9fmOqVT1psxpSlTn1pe0CO+fnJHKRLOWWac5nKxOxvOpD/w84hk1I+EydrJp7SA==", - "dependencies": { - "ua-parser-js": "^1.0.2" - }, - "peerDependencies": { - "react": ">= 0.14.0", - "react-dom": ">= 0.14.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-fast-marquee": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/react-fast-marquee/-/react-fast-marquee-1.3.1.tgz", - "integrity": "sha512-JUlQMU+IVVNKV+D4BRfRaNEaBj+VyHcI0uupBKyeFhkSY2GBkKw7oGvpNdCkPtKd8Q3H0M5eY7PyUj7AdmKCRA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" - } - }, - "node_modules/react-focus-lock": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.1.tgz", - "integrity": "sha512-pSWOQrUmiKLkffPO6BpMXN7SNKXMsuOakl652IBuALAu1esk+IcpJyM+ALcYzPTTFz1rD0R54aB9A4HuP5t1Wg==", - "dependencies": { - "@babel/runtime": "^7.0.0", - "focus-lock": "^0.11.2", - "prop-types": "^15.6.2", - "react-clientside-effect": "^1.2.6", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-google-recaptcha": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.1.0.tgz", - "integrity": "sha512-K9jr7e0CWFigi8KxC3WPvNqZZ47df2RrMAta6KmRoE4RUi7Ys6NmNjytpXpg4HI/svmQJLKR+PncEPaNJ98DqQ==", - "dependencies": { - "prop-types": "^15.5.0", - "react-async-script": "^1.1.1" - }, - "peerDependencies": { - "react": ">=16.4.1" - } - }, - "node_modules/react-google-recaptcha-v3": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/react-google-recaptcha-v3/-/react-google-recaptcha-v3-1.10.0.tgz", - "integrity": "sha512-JBoqU107X8klQmS8tQSbQh1IMsT1fH3kVoArIqnia0rtn0rPNG9Ld+9rD/dHJMculIczSZpGvIJTXXwtsolMcg==", - "dependencies": { - "hoist-non-react-statics": "^3.3.2" - }, - "peerDependencies": { - "react": "^17.0 || ^18.0", - "react-dom": "^17.0 || ^18.0" - } - }, - "node_modules/react-hook-form": { - "version": "7.30.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.30.0.tgz", - "integrity": "sha512-DzjiM6o2vtDGNMB9I4yCqW8J21P314SboNG1O0obROkbg7KVS0I7bMtwSdKyapnCPjHgnxc3L7E5PEdISeEUcQ==", - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-hook-form" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18" - } - }, - "node_modules/react-intersection-observer": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.33.1.tgz", - "integrity": "sha512-3v+qaJvp3D1MlGHyM+KISVg/CMhPiOlO6FgPHcluqHkx4YFCLuyXNlQ/LE6UkbODXlQcLOppfX6UMxCEkUhDLw==", - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-mailchimp-subscribe": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/react-mailchimp-subscribe/-/react-mailchimp-subscribe-2.1.3.tgz", - "integrity": "sha512-ZRuPZMnX/9pHQLnAQavsgB5xIF+gNqjNCCq1vvTs23cn+93W2oOp17qjg3LpDBEt1HJi6IHXMwpKXn0taY8FHw==", - "dependencies": { - "jsonp": "^0.2.1", - "prop-types": "^15.5.10", - "to-querystring": "^1.0.4" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-merge-refs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz", - "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/react-query": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.35.0.tgz", - "integrity": "sha512-mRBJdpELLV+snzyXDsXM5ZEXM+HYt05L7st6ihSXr3nHX+4ULFr30mDjsziZvx0oF5dhlSDB8aMdvG6Ah4Bukg==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "broadcast-channel": "^3.4.1", - "match-sorter": "^6.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.3.tgz", - "integrity": "sha512-NQ1bXrxKrnK5pFo/GhLkXeo3CrK5steI+5L+jynwwIemvZyfXqaL0L5BzwJd7CSwNCU723DZaccvjuyOdoy3Xw==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.1", - "react-style-singleton": "^2.2.0", - "tslib": "^2.0.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.1.tgz", - "integrity": "sha512-IvGX3mJclEF7+hga8APZczve1UyGMkMG+tjS0o/U1iLgvZRpjFAQEUBJ4JETfvbNlfNnZnoDyWJCICkA15Mghg==", - "dependencies": { - "react-style-singleton": "^2.2.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-select": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.3.2.tgz", - "integrity": "sha512-W6Irh7U6Ha7p5uQQ2ZnemoCQ8mcfgOtHfw3wuMzG6FAu0P+CYicgofSLOq97BhjMx8jS+h+wwWdCBeVVZ9VqlQ==", - "dependencies": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", - "@emotion/react": "^11.8.1", - "@types/react-transition-group": "^4.4.0", - "memoize-one": "^5.0.0", - "prop-types": "^15.6.0", - "react-transition-group": "^4.3.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.0.tgz", - "integrity": "sha512-nK7mN92DMYZEu3cQcAhfwE48NpzO5RpxjG4okbSqRRbfal9Pk+fG2RdQXTMp+f6all1hB9LIJSt+j7dCYrU11g==", - "deprecated": "wrong managing of dynamic styles", - "dependencies": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-transition-group": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", - "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", - "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" - } - }, - "node_modules/react-use-measure": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", - "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", - "dependencies": { - "debounce": "^1.2.1" - }, - "peerDependencies": { - "react": ">=16.13", - "react-dom": ">=16.13" - } - }, - "node_modules/react-youtube": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/react-youtube/-/react-youtube-9.0.2.tgz", - "integrity": "sha512-qgNXo+axgsWtEqZlesSy+ruV2xaDW2NQFTFx0zqfeA3QyE8QEsyLMuTCF6aC4LyMMf+LsHCSGQw2gTngVkb6NQ==", - "dependencies": { - "fast-deep-equal": "3.1.3", - "prop-types": "15.8.1", - "youtube-player": "5.5.2" - }, - "engines": { - "node": ">= 14.x" - }, - "peerDependencies": { - "react": ">=0.14.1" - } - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/redent/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", - "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/relay-runtime": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz", - "integrity": "sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==", - "dependencies": { - "@babel/runtime": "^7.0.0", - "fbjs": "^3.0.0", - "invariant": "^2.2.4" - } - }, - "node_modules/remedial": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", - "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/remove-accents": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", - "integrity": "sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U= sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8= sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true - }, - "node_modules/remove-trailing-spaces": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", - "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==", - "dev": true - }, - "node_modules/replaceall": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/replaceall/-/replaceall-0.1.6.tgz", - "integrity": "sha1-gdgax663LX9cSUKt8ml6MiBojY4= sha512-sL26E4+8Kec7bwpRjHlQvbNZcpnGroT3PA7ywsgH6GjzxAg4IGNlNalLoRC/JmTed7cMhyDbi44pWw1kMhDxlw==", - "dev": true, - "engines": { - "node": ">= 0.8.x" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I= sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dependencies": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", - "dev": true, - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/sass": { - "version": "1.50.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.50.1.tgz", - "integrity": "sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==", - "devOptional": true, - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/scuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/scuid/-/scuid-1.1.0.tgz", - "integrity": "sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==", - "dev": true - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sentence-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", - "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc= sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" - }, - "node_modules/sharp": { - "version": "0.30.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.4.tgz", - "integrity": "sha512-3Onig53Y6lji4NIZo69s14mERXXY/GV++6CzOYx/Rd8bnTwbhFbL09WZd7Ag/CCnA0WxFID8tkY0QReyfL6v0Q==", - "hasInstallScript": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^4.3.0", - "prebuild-install": "^7.0.1", - "semver": "^7.3.7", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" - }, - "engines": { - "node": ">=12.13.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/signedsource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz", - "integrity": "sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo= sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-get/node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/simple-get/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/sirv": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", - "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", - "dev": true, - "dependencies": { - "@polka/url": "^1.0.0-next.20", - "mrmime": "^1.0.0", - "totalist": "^1.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/sister": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sister/-/sister-3.0.2.tgz", - "integrity": "sha512-p19rtTs+NksBRKW9qn0UhZ8/TUI9BPw9lmtHny+Y3TinWlOa9jWh9xB0AtPSdmOy49NJJJSSe0Ey4C7h0TrcYA==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", - "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, - "node_modules/specificity": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", - "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", - "dev": true, - "bin": { - "specificity": "bin/specificity" - } - }, - "node_modules/sponge-case": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz", - "integrity": "sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true, - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-env-interpolation": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz", - "integrity": "sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/string-width/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-search": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI= sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", - "dev": true - }, - "node_modules/styled-jsx": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.4.tgz", - "integrity": "sha512-sDFWLbg4zR+UkNzfk5lPilyIgtpddfxXEULxhujorr5jtePTUqiPDc5BC0v1NRqTr/WaFBGQQUoYToGlF4B2KQ==", - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/stylelint": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.7.1.tgz", - "integrity": "sha512-rUOWm67hrzGXXyO/cInENEejF4urh1dLgOb9cr/3XLDb/t/A+rXQp3p6+no8o8QCKTgBUdhVUq/bXMgE988PJw==", - "dev": true, - "dependencies": { - "balanced-match": "^2.0.0", - "colord": "^2.9.2", - "cosmiconfig": "^7.0.1", - "css-functions-list": "^3.0.1", - "debug": "^4.3.4", - "execall": "^2.0.0", - "fast-glob": "^3.2.11", - "fastest-levenshtein": "^1.0.12", - "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.2.0", - "ignore": "^5.2.0", - "import-lazy": "^4.0.0", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.24.0", - "mathml-tag-names": "^2.1.3", - "meow": "^9.0.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "normalize-selector": "^0.2.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.12", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.10", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "specificity": "^0.4.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "style-search": "^0.1.0", - "supports-hyperlinks": "^2.2.0", - "svg-tags": "^1.0.0", - "table": "^6.8.0", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^4.0.1" - }, - "bin": { - "stylelint": "bin/stylelint.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - } - }, - "node_modules/stylelint-config-prettier": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", - "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", - "dev": true, - "bin": { - "stylelint-config-prettier": "bin/check.js", - "stylelint-config-prettier-check": "bin/check.js" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "stylelint": ">=11.0.0" - } - }, - "node_modules/stylelint-config-recommended": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", - "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", - "dev": true, - "peerDependencies": { - "stylelint": "^14.4.0" - } - }, - "node_modules/stylelint-config-recommended-scss": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz", - "integrity": "sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg==", - "dev": true, - "dependencies": { - "postcss-scss": "^4.0.2", - "stylelint-config-recommended": "^6.0.0", - "stylelint-scss": "^4.0.0" - }, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-recommended-scss/node_modules/stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-standard": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", - "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", - "dev": true, - "dependencies": { - "stylelint-config-recommended": "^7.0.0" - }, - "peerDependencies": { - "stylelint": "^14.4.0" - } - }, - "node_modules/stylelint-config-standard-scss": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz", - "integrity": "sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw==", - "dev": true, - "dependencies": { - "stylelint-config-recommended-scss": "^5.0.2", - "stylelint-config-standard": "^24.0.0" - }, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-config-standard-scss/node_modules/stylelint-config-standard": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz", - "integrity": "sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==", - "dev": true, - "dependencies": { - "stylelint-config-recommended": "^6.0.0" - }, - "peerDependencies": { - "stylelint": "^14.0.0" - } - }, - "node_modules/stylelint-prettier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stylelint-prettier/-/stylelint-prettier-2.0.0.tgz", - "integrity": "sha512-jvT3G+9lopkeB0ARmDPszyfaOnvnIF+30QCjZxyt7E6fynI1T9mOKgYDNb9bXX17M7PXMZaX3j/26wqakjp1tw==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "prettier": ">=2.0.0", - "stylelint": ">=14.0.0" - } - }, - "node_modules/stylelint-scss": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.2.0.tgz", - "integrity": "sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-selector-parser": "^6.0.6", - "postcss-value-parser": "^4.1.0" - }, - "peerDependencies": { - "stylelint": "^14.5.1" - } - }, - "node_modules/stylelint/node_modules/balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", - "dev": true - }, - "node_modules/stylis": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", - "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" - }, - "node_modules/subscriptions-transport-ws": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", - "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", - "deprecated": "The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md", - "dev": true, - "dependencies": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependencies": { - "graphql": "^15.7.2 || ^16.0.0" - } - }, - "node_modules/subscriptions-transport-ws/node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", - "dev": true - }, - "node_modules/swap-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz", - "integrity": "sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sync-fetch": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.3.1.tgz", - "integrity": "sha512-xj5qiCDap/03kpci5a+qc5wSJjc8ZSixgG2EUmH1B8Ea2sfWclQA7eH40hiHPCtkCn6MCk4Wb+dqcXdCy2PP3g==", - "dev": true, - "dependencies": { - "buffer": "^5.7.0", - "node-fetch": "^2.6.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/table/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/tiny-json-http": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/tiny-json-http/-/tiny-json-http-7.4.2.tgz", - "integrity": "sha512-+3ns4PfQTLaF69zGASkAfDoOEVmwYTXSDrU6VR93h317uFOW7evFzKa7Ih9JzPHiYSee3lUXHLAGhws2wFSexQ==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, - "node_modules/title-case": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", - "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-querystring": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/to-querystring/-/to-querystring-1.1.1.tgz", - "integrity": "sha512-ZgIacl9TXAoT7sGXUYjQiy0MW7Tf/7CJQLt757hYHfXXc8JBzOVBMx4DckqKUO4hi36J72/m8UcH/GCHK+n97g==" - }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-log": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.4.tgz", - "integrity": "sha512-DEQrfv6l7IvN2jlzc/VTdZJYsWUnQNCsueYjMkC/iXoEoi5fNan6MjeDqkvhfzbmHgdz9UxDUluX3V5HdjTydQ==", - "dev": true - }, - "node_modules/ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "typescript": ">=2.7" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", - "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ua-parser-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.2.tgz", - "integrity": "sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo= sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/undici": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.0.0.tgz", - "integrity": "sha512-VhUpiZ3No1DOPPQVQnsDZyfcbTTcHdcgWej1PdFnSvOeJmOVDgiOHkunJmBLfmjt4CqgPQddPVjSWW0dsTs5Yg==", - "dev": true, - "engines": { - "node": ">=12.18" - } - }, - "node_modules/universal-base64": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/universal-base64/-/universal-base64-2.1.0.tgz", - "integrity": "sha512-WeOkACVnIXJZr/qlv7++Rl1zuZOHN96v2yS5oleUuv8eJOs5j9M5U3xQEIoWqn1OzIuIcgw0fswxWnUVGDfW6g==" - }, - "node_modules/unixify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz", - "integrity": "sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA= sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==", - "dev": true, - "dependencies": { - "normalize-path": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unixify/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unload": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", - "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", - "dependencies": { - "@babel/runtime": "^7.6.2", - "detect-node": "^2.0.4" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", - "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/upper-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", - "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", - "dev": true, - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", - "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-deep-compare-effect": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/use-deep-compare-effect/-/use-deep-compare-effect-1.8.1.tgz", - "integrity": "sha512-kbeNVZ9Zkc0RFGpfMN3MNfaKNvcLNyxOAAd9O4CBZ+kCBXXscn9s/4I+8ytUER4RDpEYs5+O6Rs4PqiZ+rHr5Q==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "dequal": "^2.0.2" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - }, - "peerDependencies": { - "react": ">=16.13" - } - }, - "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/valid-url": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", - "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/value-or-promise": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz", - "integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/vhtml": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vhtml/-/vhtml-2.2.0.tgz", - "integrity": "sha512-TPXrXrxBOslRUVnlVkiAqhoXneiertIg86bdvzionrUYhEuiROvyPZNiiP6GIIJ2Q7oPNVyEtIx8gMAZZE9lCQ==" - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/webpack-bundle-analyzer": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz", - "integrity": "sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA==", - "dev": true, - "dependencies": { - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", - "commander": "^6.2.0", - "gzip-size": "^6.0.0", - "lodash": "^4.17.20", - "opener": "^1.5.2", - "sirv": "^1.0.7", - "ws": "^7.3.1" - }, - "bin": { - "webpack-bundle-analyzer": "lib/bin/analyzer.js" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", - "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" - } - }, - "node_modules/ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yaml-ast-parser": { - "version": "0.0.43", - "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", - "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/youtube-player": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/youtube-player/-/youtube-player-5.5.2.tgz", - "integrity": "sha512-ZGtsemSpXnDky2AUYWgxjaopgB+shFHgXVpiJFeNB5nWEugpW1KWYDaHKuLqh2b67r24GtP6HoSW5swvf0fFIQ==", - "dependencies": { - "debug": "^2.6.6", - "load-script": "^1.0.0", - "sister": "^3.0.0" - } - }, - "node_modules/youtube-player/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/youtube-player/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/zod": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.17.3.tgz", - "integrity": "sha512-4oKP5zvG6GGbMlqBkI5FESOAweldEhSOZ6LI6cG+JzUT7ofj1ZOC0PJudpQOpT1iqOFpYYtX5Pw0+o403y4bcg==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - }, "dependencies": { "@ampproject/remapping": { "version": "2.1.2", @@ -13135,12 +1352,27 @@ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz", "integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==" }, + "@mailchimp/mailchimp_marketing": { + "version": "3.0.80", + "resolved": "https://registry.npmjs.org/@mailchimp/mailchimp_marketing/-/mailchimp_marketing-3.0.80.tgz", + "integrity": "sha512-Cgz0xPb+1DUjmrl5whAsmqfAChBko+Wf4/PLQE4RvwfPlcq2agfHr1QFiXEhZ8e+GQwQ3hZQn9iLGXwIXwxUCg==", + "requires": { + "dotenv": "^8.2.0", + "superagent": "3.8.1" + }, + "dependencies": { + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" + } + } + }, "@n1ru4l/graphql-live-query": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz", "integrity": "sha512-BTpWy1e+FxN82RnLz4x1+JcEewVdfmUhV1C6/XYD5AjS7PQp9QFF7K8bCD6gzPTr2l+prvqOyVueQhFJxB1vfg==", - "dev": true, - "requires": {} + "dev": true }, "@next/bundle-analyzer": { "version": "12.1.5", @@ -13496,8 +1728,7 @@ "@radix-ui/react-polymorphic": { "version": "0.0.14", "resolved": "https://registry.npmjs.org/@radix-ui/react-polymorphic/-/react-polymorphic-0.0.14.tgz", - "integrity": "sha512-9nsMZEDU3LeIUeHJrpkkhZVxu/9Fc7P2g2I3WR+uA9mTbNC3hGaabi0dV6wg0CfHb+m4nSs1pejbE/5no3MJTA==", - "requires": {} + "integrity": "sha512-9nsMZEDU3LeIUeHJrpkkhZVxu/9Fc7P2g2I3WR+uA9mTbNC3hGaabi0dV6wg0CfHb+m4nSs1pejbE/5no3MJTA==" }, "@radix-ui/react-presence": { "version": "0.1.2", @@ -13656,6 +1887,12 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==" }, + "@types/mailchimp__mailchimp_marketing": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/mailchimp__mailchimp_marketing/-/mailchimp__mailchimp_marketing-3.0.8.tgz", + "integrity": "sha512-eis38/TBVkln2wd5yKG4fr6RdBR0FsbCIW8j4tVwJf/eczrjP671eecNJ5JNgU9FiWIqkvXgaBWP5Zh3GYl8CQ==", + "dev": true + }, "@types/marked": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.3.tgz", @@ -13716,15 +1953,6 @@ "@types/react": "^17" } }, - "@types/react-google-recaptcha": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/react-google-recaptcha/-/react-google-recaptcha-2.1.5.tgz", - "integrity": "sha512-iWTjmVttlNgp0teyh7eBXqNOQzVq2RWNiFROWjraOptRnb1OcHJehQnji0sjqIRAk9K0z8stjyhU+OLpPb0N6w==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, "@types/react-transition-group": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", @@ -13876,8 +2104,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "8.2.0", @@ -13956,7 +2183,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "devOptional": true, + "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -14214,7 +2441,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "devOptional": true + "dev": true }, "bl": { "version": "4.1.0", @@ -14249,7 +2476,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "devOptional": true, + "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -14455,7 +2682,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "devOptional": true, + "dev": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -14644,6 +2871,11 @@ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==" }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -14679,6 +2911,11 @@ } } }, + "cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" + }, "core-js-pure": { "version": "3.21.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", @@ -15406,8 +3643,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", - "dev": true, - "requires": {} + "dev": true }, "minimatch": { "version": "3.1.2", @@ -15430,8 +3666,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} + "dev": true }, "eslint-import-resolver-node": { "version": "0.3.4", @@ -15679,15 +3914,13 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz", "integrity": "sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-simple-import-sort": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz", "integrity": "sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==", - "dev": true, - "requires": {} + "dev": true }, "eslint-scope": { "version": "5.1.1", @@ -15814,6 +4047,11 @@ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -15946,7 +4184,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "devOptional": true, + "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -16023,6 +4261,11 @@ } } }, + "formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -16178,7 +4421,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, + "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -16297,8 +4540,7 @@ "version": "0.0.22", "resolved": "https://registry.npmjs.org/graphql-executor/-/graphql-executor-0.0.22.tgz", "integrity": "sha512-WbKSnSHFn6REKKH4T6UAwDM3mLUnYMQlQLNG0Fw+Lkb3ilCnL3m5lkJ7411LAI9sF7BvPbthovVZhsEUh9Xfag==", - "dev": true, - "requires": {} + "dev": true }, "graphql-request": { "version": "4.2.0", @@ -16321,8 +4563,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/graphql-sse/-/graphql-sse-1.1.0.tgz", "integrity": "sha512-xE8AGPJa5X+g7iFmRQw/8H+7lXIDJvSkW6lou/XSSq17opPQl+dbKOMiqraHMx52VrDgS061ZVx90OSuqS6ykA==", - "dev": true, - "requires": {} + "dev": true }, "graphql-tag": { "version": "2.12.6", @@ -16336,8 +4577,7 @@ "version": "5.7.0", "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.7.0.tgz", "integrity": "sha512-8yYuvnyqIjlJ/WfebOyu2GSOQeFauRxnfuTveY9yvrDGs2g3kR9Nv4gu40AKvRHbXlSJwTbMJ6dVxAtEyKwVRA==", - "dev": true, - "requires": {} + "dev": true }, "gsap": { "version": "file:src/lib/gsap/gsap-bonus.tgz", @@ -16479,9 +4719,9 @@ "dev": true }, "husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", "dev": true }, "iconv-lite": { @@ -16508,7 +4748,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", - "devOptional": true + "dev": true }, "import-fresh": { "version": "3.3.0", @@ -16636,7 +4876,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, + "dev": true, "requires": { "binary-extensions": "^2.0.0" } @@ -16678,7 +4918,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true + "dev": true }, "is-fullwidth-code-point": { "version": "4.0.0", @@ -16690,7 +4930,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, + "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -16719,7 +4959,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true + "dev": true }, "is-number-object": { "version": "1.0.7", @@ -16875,8 +5115,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", - "dev": true, - "requires": {} + "dev": true }, "iterall": { "version": "1.3.0", @@ -17818,8 +6057,12 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/meros/-/meros-1.2.0.tgz", "integrity": "sha512-3QRZIS707pZQnijHdhbttXRWwrHhZJ/gzolneoxKVz9N/xmsvY/7Ls8lpnI9gxbgxjcHsAVEW3mgwiZCo6kkJQ==", - "dev": true, - "requires": {} + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "micromatch": { "version": "4.0.5", @@ -17836,6 +6079,11 @@ "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -17979,14 +6227,12 @@ "next-real-viewport": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/next-real-viewport/-/next-real-viewport-0.7.0.tgz", - "integrity": "sha512-Hs9+J+S4y1CaJ+t7v46/G54MuUYPdUyyctOrYUcGd299FFHSDjq4VCjhtc9vcY4LE+OpnY4BXZ/+ENUSJ/JFQg==", - "requires": {} + "integrity": "sha512-Hs9+J+S4y1CaJ+t7v46/G54MuUYPdUyyctOrYUcGd299FFHSDjq4VCjhtc9vcY4LE+OpnY4BXZ/+ENUSJ/JFQg==" }, "next-seo": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/next-seo/-/next-seo-5.4.0.tgz", - "integrity": "sha512-R9DhajPwJnR/lsF2hZ8cN8uqr5CVITsRrCG1AF5+ufcaybKYOvnH8sH9MaH4/hpkps3PQ9H71S7J7SPYixAYzQ==", - "requires": {} + "integrity": "sha512-R9DhajPwJnR/lsF2hZ8cN8uqr5CVITsRrCG1AF5+ufcaybKYOvnH8sH9MaH4/hpkps3PQ9H71S7J7SPYixAYzQ==" }, "next-sitemap": { "version": "2.5.20", @@ -18001,8 +6247,7 @@ "next-themes": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.0.tgz", - "integrity": "sha512-myhpDL4vadBD9YDSHiewqvzorGzB03N84e+3LxCwHRlM/hiBOaW+UsKsQojQAzC7fdcJA0l2ppveXcYaVV+hxQ==", - "requires": {} + "integrity": "sha512-myhpDL4vadBD9YDSHiewqvzorGzB03N84e+3LxCwHRlM/hiBOaW+UsKsQojQAzC7fdcJA0l2ppveXcYaVV+hxQ==" }, "no-case": { "version": "3.0.4", @@ -18066,7 +6311,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true + "dev": true }, "normalize-selector": { "version": "0.2.0", @@ -18118,8 +6363,7 @@ "object-inspect": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "dev": true + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" }, "object-keys": { "version": "1.1.1", @@ -18422,7 +6666,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true + "dev": true }, "pidtree": { "version": "0.5.0", @@ -18456,15 +6700,13 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-scss": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.3.tgz", "integrity": "sha512-j4KxzWovfdHsyxwl1BxkUal/O4uirvHgdzMKS1aWJBAV0qh2qj5qAZqpeBfVUYGWv+4iK9Az7SPyZ4fyNju1uA==", - "dev": true, - "requires": {} + "dev": true }, "postcss-selector-parser": { "version": "6.0.10", @@ -18567,6 +6809,14 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -18660,8 +6910,7 @@ "react-fast-marquee": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/react-fast-marquee/-/react-fast-marquee-1.3.1.tgz", - "integrity": "sha512-JUlQMU+IVVNKV+D4BRfRaNEaBj+VyHcI0uupBKyeFhkSY2GBkKw7oGvpNdCkPtKd8Q3H0M5eY7PyUj7AdmKCRA==", - "requires": {} + "integrity": "sha512-JUlQMU+IVVNKV+D4BRfRaNEaBj+VyHcI0uupBKyeFhkSY2GBkKw7oGvpNdCkPtKd8Q3H0M5eY7PyUj7AdmKCRA==" }, "react-focus-lock": { "version": "2.9.1", @@ -18696,14 +6945,12 @@ "react-hook-form": { "version": "7.30.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.30.0.tgz", - "integrity": "sha512-DzjiM6o2vtDGNMB9I4yCqW8J21P314SboNG1O0obROkbg7KVS0I7bMtwSdKyapnCPjHgnxc3L7E5PEdISeEUcQ==", - "requires": {} + "integrity": "sha512-DzjiM6o2vtDGNMB9I4yCqW8J21P314SboNG1O0obROkbg7KVS0I7bMtwSdKyapnCPjHgnxc3L7E5PEdISeEUcQ==" }, "react-intersection-observer": { "version": "8.33.1", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.33.1.tgz", - "integrity": "sha512-3v+qaJvp3D1MlGHyM+KISVg/CMhPiOlO6FgPHcluqHkx4YFCLuyXNlQ/LE6UkbODXlQcLOppfX6UMxCEkUhDLw==", - "requires": {} + "integrity": "sha512-3v+qaJvp3D1MlGHyM+KISVg/CMhPiOlO6FgPHcluqHkx4YFCLuyXNlQ/LE6UkbODXlQcLOppfX6UMxCEkUhDLw==" }, "react-is": { "version": "16.13.1", @@ -18929,7 +7176,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, + "dev": true, "requires": { "picomatch": "^2.2.1" } @@ -19145,7 +7392,7 @@ "version": "1.50.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.50.1.tgz", "integrity": "sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==", - "devOptional": true, + "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -19238,7 +7485,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -19423,14 +7669,6 @@ "tslib": "^2.0.3" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, "string-argv": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", @@ -19501,6 +7739,14 @@ "define-properties": "^1.1.3" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -19545,8 +7791,7 @@ "styled-jsx": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.4.tgz", - "integrity": "sha512-sDFWLbg4zR+UkNzfk5lPilyIgtpddfxXEULxhujorr5jtePTUqiPDc5BC0v1NRqTr/WaFBGQQUoYToGlF4B2KQ==", - "requires": {} + "integrity": "sha512-sDFWLbg4zR+UkNzfk5lPilyIgtpddfxXEULxhujorr5jtePTUqiPDc5BC0v1NRqTr/WaFBGQQUoYToGlF4B2KQ==" }, "stylelint": { "version": "14.7.1", @@ -19609,15 +7854,13 @@ "version": "9.0.3", "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", - "dev": true, - "requires": {} + "dev": true }, "stylelint-config-recommended": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", - "dev": true, - "requires": {} + "dev": true }, "stylelint-config-recommended-scss": { "version": "5.0.2", @@ -19634,8 +7877,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -19662,8 +7904,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", - "dev": true, - "requires": {} + "dev": true }, "stylelint-config-standard": { "version": "24.0.0", @@ -19720,8 +7961,71 @@ "version": "7.5.7", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "requires": {} + "dev": true + } + } + }, + "superagent": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.1.tgz", + "integrity": "sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==", + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } } } }, @@ -19910,7 +8214,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, + "dev": true, "requires": { "is-number": "^7.0.0" } @@ -20169,8 +8473,7 @@ "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "requires": {} + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" }, "util-deprecate": { "version": "1.0.2", @@ -20257,8 +8560,7 @@ "version": "7.5.7", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -20348,8 +8650,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true, - "requires": {} + "dev": true }, "y18n": { "version": "5.0.8", diff --git a/package.json b/package.json index 1698683..9457a47 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "eslint-plugin-react": "7.29.4", "eslint-plugin-react-hooks": "4.4.0", "eslint-plugin-simple-import-sort": "^7.0.0", - "husky": "^7.0.4", + "husky": "^8.0.0", "lint-staged": "^12.3.8", "next-compose-plugins": "^2.2.1", "next-sitemap": "^2.5.20", @@ -154,8 +154,8 @@ "@typescript-eslint/no-var-requires": 0, "@typescript-eslint/no-use-before-define": 0, "@typescript-eslint/ban-ts-comment": 0, - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", + "simple-import-sort/imports": "warn", + "simple-import-sort/exports": "warn", "react-hooks/exhaustive-deps": "warn", "react/no-unescaped-entities": 0, "curly": [ diff --git a/public/assets/laconic-logo-assets.zip b/public/assets/laconic-logo-assets.zip new file mode 100644 index 0000000..cd7dd92 Binary files /dev/null and b/public/assets/laconic-logo-assets.zip differ diff --git a/public/images/site_content/aboutPage/whitepaperImage.png b/public/images/site_content/aboutPage/whitepaperImage.png new file mode 100644 index 0000000..90d5919 Binary files /dev/null and b/public/images/site_content/aboutPage/whitepaperImage.png differ diff --git a/public/images/site_content/blogPost/99-problems-but-nfts-aint-one.png b/public/images/site_content/blogPost/99-problems-but-nfts-aint-one.png new file mode 100644 index 0000000..5fe9372 Binary files /dev/null and b/public/images/site_content/blogPost/99-problems-but-nfts-aint-one.png differ diff --git a/public/images/site_content/blogPost/_default.png b/public/images/site_content/blogPost/_default.png new file mode 100644 index 0000000..62b31fb Binary files /dev/null and b/public/images/site_content/blogPost/_default.png differ diff --git a/public/images/site_content/blogPost/erc20-watcher-demo.png b/public/images/site_content/blogPost/erc20-watcher-demo.png new file mode 100644 index 0000000..5cabf3a Binary files /dev/null and b/public/images/site_content/blogPost/erc20-watcher-demo.png differ diff --git a/public/images/site_content/blogPost/how-laconic-different.png b/public/images/site_content/blogPost/how-laconic-different.png new file mode 100644 index 0000000..f1a68b1 Binary files /dev/null and b/public/images/site_content/blogPost/how-laconic-different.png differ diff --git a/public/images/site_content/blogPost/how-laconic-improves-the-nft-experience.png b/public/images/site_content/blogPost/how-laconic-improves-the-nft-experience.png new file mode 100644 index 0000000..d3bf529 Binary files /dev/null and b/public/images/site_content/blogPost/how-laconic-improves-the-nft-experience.png differ diff --git a/public/images/site_content/blogPost/how-laconic-network-uses-ipld.png b/public/images/site_content/blogPost/how-laconic-network-uses-ipld.png new file mode 100644 index 0000000..dd00040 Binary files /dev/null and b/public/images/site_content/blogPost/how-laconic-network-uses-ipld.png differ diff --git a/public/images/site_content/blogPost/intro-to-the-laconic-stack.png b/public/images/site_content/blogPost/intro-to-the-laconic-stack.png new file mode 100644 index 0000000..f1f4505 Binary files /dev/null and b/public/images/site_content/blogPost/intro-to-the-laconic-stack.png differ diff --git a/public/images/site_content/blogPost/introducing-laconic-network.png b/public/images/site_content/blogPost/introducing-laconic-network.png new file mode 100644 index 0000000..9436111 Binary files /dev/null and b/public/images/site_content/blogPost/introducing-laconic-network.png differ diff --git a/public/images/site_content/blogPost/laconic-and-consensys-metamask-launch-mobymask-light-client.png b/public/images/site_content/blogPost/laconic-and-consensys-metamask-launch-mobymask-light-client.png new file mode 100644 index 0000000..ee6413d Binary files /dev/null and b/public/images/site_content/blogPost/laconic-and-consensys-metamask-launch-mobymask-light-client.png differ diff --git a/public/images/site_content/blogPost/laconic-devcon-vi-recap.png b/public/images/site_content/blogPost/laconic-devcon-vi-recap.png new file mode 100644 index 0000000..bf5d2e0 Binary files /dev/null and b/public/images/site_content/blogPost/laconic-devcon-vi-recap.png differ diff --git a/public/images/site_content/blogPost/laconic-governance-model.png b/public/images/site_content/blogPost/laconic-governance-model.png new file mode 100644 index 0000000..bfa814a Binary files /dev/null and b/public/images/site_content/blogPost/laconic-governance-model.png differ diff --git a/public/images/site_content/blogPost/laconic-watchers.png b/public/images/site_content/blogPost/laconic-watchers.png new file mode 100644 index 0000000..8b5bfd8 Binary files /dev/null and b/public/images/site_content/blogPost/laconic-watchers.png differ diff --git a/public/images/site_content/blogPost/monthly-update-dec-2022.png b/public/images/site_content/blogPost/monthly-update-dec-2022.png new file mode 100644 index 0000000..1d23124 Binary files /dev/null and b/public/images/site_content/blogPost/monthly-update-dec-2022.png differ diff --git a/public/images/site_content/blogPost/rick-dudley-on-interchain-fm.png b/public/images/site_content/blogPost/rick-dudley-on-interchain-fm.png new file mode 100644 index 0000000..4bc1d45 Binary files /dev/null and b/public/images/site_content/blogPost/rick-dudley-on-interchain-fm.png differ diff --git a/public/images/site_content/blogPost/rick-dudley-on-the-interop.png b/public/images/site_content/blogPost/rick-dudley-on-the-interop.png new file mode 100644 index 0000000..95e0051 Binary files /dev/null and b/public/images/site_content/blogPost/rick-dudley-on-the-interop.png differ diff --git a/public/images/site_content/blogPost/test-blog-post.png b/public/images/site_content/blogPost/test-blog-post.png new file mode 100644 index 0000000..62b31fb Binary files /dev/null and b/public/images/site_content/blogPost/test-blog-post.png differ diff --git a/public/images/site_content/blogPost/trends.png b/public/images/site_content/blogPost/trends.png new file mode 100644 index 0000000..c59542d Binary files /dev/null and b/public/images/site_content/blogPost/trends.png differ diff --git a/public/images/site_content/blogPost/what-is-a-proof.png b/public/images/site_content/blogPost/what-is-a-proof.png new file mode 100644 index 0000000..d4d2df9 Binary files /dev/null and b/public/images/site_content/blogPost/what-is-a-proof.png differ diff --git a/public/images/site_content/blogPost/why-decentralization-matters.png b/public/images/site_content/blogPost/why-decentralization-matters.png new file mode 100644 index 0000000..94aa0bf Binary files /dev/null and b/public/images/site_content/blogPost/why-decentralization-matters.png differ diff --git a/public/images/site_content/careersPage/valuesPoint01Image.png b/public/images/site_content/careersPage/valuesPoint01Image.png new file mode 100644 index 0000000..c488510 Binary files /dev/null and b/public/images/site_content/careersPage/valuesPoint01Image.png differ diff --git a/public/images/site_content/careersPage/valuesPoint01ImageLight.png b/public/images/site_content/careersPage/valuesPoint01ImageLight.png new file mode 100644 index 0000000..058b638 Binary files /dev/null and b/public/images/site_content/careersPage/valuesPoint01ImageLight.png differ diff --git a/public/images/site_content/careersPage/valuesPoint02Image.png b/public/images/site_content/careersPage/valuesPoint02Image.png new file mode 100644 index 0000000..25b6227 Binary files /dev/null and b/public/images/site_content/careersPage/valuesPoint02Image.png differ diff --git a/public/images/site_content/careersPage/valuesPoint02ImageLight.png b/public/images/site_content/careersPage/valuesPoint02ImageLight.png new file mode 100644 index 0000000..1f523c5 Binary files /dev/null and b/public/images/site_content/careersPage/valuesPoint02ImageLight.png differ diff --git a/public/images/site_content/careersPage/valuesPoint03Image.png b/public/images/site_content/careersPage/valuesPoint03Image.png new file mode 100644 index 0000000..7121e54 Binary files /dev/null and b/public/images/site_content/careersPage/valuesPoint03Image.png differ diff --git a/public/images/site_content/careersPage/valuesPoint03ImageLight.png b/public/images/site_content/careersPage/valuesPoint03ImageLight.png new file mode 100644 index 0000000..3a103d8 Binary files /dev/null and b/public/images/site_content/careersPage/valuesPoint03ImageLight.png differ diff --git a/public/images/site_content/careersPage/valuesPoint04Image.png b/public/images/site_content/careersPage/valuesPoint04Image.png new file mode 100644 index 0000000..3b70e96 Binary files /dev/null and b/public/images/site_content/careersPage/valuesPoint04Image.png differ diff --git a/public/images/site_content/careersPage/valuesPoint04ImageLight.png b/public/images/site_content/careersPage/valuesPoint04ImageLight.png new file mode 100644 index 0000000..189543b Binary files /dev/null and b/public/images/site_content/careersPage/valuesPoint04ImageLight.png differ diff --git a/public/images/site_content/careersPage/whyPoint01Img.png b/public/images/site_content/careersPage/whyPoint01Img.png new file mode 100644 index 0000000..5f315ed Binary files /dev/null and b/public/images/site_content/careersPage/whyPoint01Img.png differ diff --git a/public/images/site_content/careersPage/whyPoint02Img.png b/public/images/site_content/careersPage/whyPoint02Img.png new file mode 100644 index 0000000..eece9a7 Binary files /dev/null and b/public/images/site_content/careersPage/whyPoint02Img.png differ diff --git a/public/images/site_content/careersPage/whyPoint03Img.png b/public/images/site_content/careersPage/whyPoint03Img.png new file mode 100644 index 0000000..f969619 Binary files /dev/null and b/public/images/site_content/careersPage/whyPoint03Img.png differ diff --git a/public/images/site_content/careersPage/whyPoint04Img.png b/public/images/site_content/careersPage/whyPoint04Img.png new file mode 100644 index 0000000..ce1a842 Binary files /dev/null and b/public/images/site_content/careersPage/whyPoint04Img.png differ diff --git a/public/images/site_content/careersPage/whyPoint05Img.png b/public/images/site_content/careersPage/whyPoint05Img.png new file mode 100644 index 0000000..81f4845 Binary files /dev/null and b/public/images/site_content/careersPage/whyPoint05Img.png differ diff --git a/public/images/site_content/careersPage/whyPoint06Img.png b/public/images/site_content/careersPage/whyPoint06Img.png new file mode 100644 index 0000000..8bfd0a8 Binary files /dev/null and b/public/images/site_content/careersPage/whyPoint06Img.png differ diff --git a/public/images/site_content/communityPage/socialsImage.png b/public/images/site_content/communityPage/socialsImage.png new file mode 100644 index 0000000..806e423 Binary files /dev/null and b/public/images/site_content/communityPage/socialsImage.png differ diff --git a/public/images/site_content/communityPage/socialsImageLight.png b/public/images/site_content/communityPage/socialsImageLight.png new file mode 100644 index 0000000..0e2eec7 Binary files /dev/null and b/public/images/site_content/communityPage/socialsImageLight.png differ diff --git a/public/images/site_content/event/20220921-mainnet.png b/public/images/site_content/event/20220921-mainnet.png new file mode 100644 index 0000000..cbecb58 Binary files /dev/null and b/public/images/site_content/event/20220921-mainnet.png differ diff --git a/public/images/site_content/event/20220927-cosmoverse.png b/public/images/site_content/event/20220927-cosmoverse.png new file mode 100644 index 0000000..c89b9bb Binary files /dev/null and b/public/images/site_content/event/20220927-cosmoverse.png differ diff --git a/public/images/site_content/event/20221007-devcon.png b/public/images/site_content/event/20221007-devcon.png new file mode 100644 index 0000000..7e483f6 Binary files /dev/null and b/public/images/site_content/event/20221007-devcon.png differ diff --git a/public/images/site_content/event/20221103-eth-sf.png b/public/images/site_content/event/20221103-eth-sf.png new file mode 100644 index 0000000..e94c603 Binary files /dev/null and b/public/images/site_content/event/20221103-eth-sf.png differ diff --git a/public/images/site_content/laconic-default.png b/public/images/site_content/laconic-default.png new file mode 100644 index 0000000..d77239a Binary files /dev/null and b/public/images/site_content/laconic-default.png differ diff --git a/public/images/site_content/partnersPage/_default.png b/public/images/site_content/partnersPage/_default.png new file mode 100644 index 0000000..8fa1f64 Binary files /dev/null and b/public/images/site_content/partnersPage/_default.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage01.png b/public/images/site_content/partnersPage/oportunitiesImage01.png new file mode 100644 index 0000000..620ad03 Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage01.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage01Light.png b/public/images/site_content/partnersPage/oportunitiesImage01Light.png new file mode 100644 index 0000000..675dde5 Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage01Light.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage02.png b/public/images/site_content/partnersPage/oportunitiesImage02.png new file mode 100644 index 0000000..1003971 Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage02.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage02Light.png b/public/images/site_content/partnersPage/oportunitiesImage02Light.png new file mode 100644 index 0000000..b328d1c Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage02Light.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage03.png b/public/images/site_content/partnersPage/oportunitiesImage03.png new file mode 100644 index 0000000..9398519 Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage03.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage03Light.png b/public/images/site_content/partnersPage/oportunitiesImage03Light.png new file mode 100644 index 0000000..4b8957e Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage03Light.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage04.png b/public/images/site_content/partnersPage/oportunitiesImage04.png new file mode 100644 index 0000000..b1eadbd Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage04.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage04Light.png b/public/images/site_content/partnersPage/oportunitiesImage04Light.png new file mode 100644 index 0000000..b5b2d00 Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage04Light.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage05.png b/public/images/site_content/partnersPage/oportunitiesImage05.png new file mode 100644 index 0000000..9421c8d Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage05.png differ diff --git a/public/images/site_content/partnersPage/oportunitiesImage05Light.png b/public/images/site_content/partnersPage/oportunitiesImage05Light.png new file mode 100644 index 0000000..ff097ba Binary files /dev/null and b/public/images/site_content/partnersPage/oportunitiesImage05Light.png differ diff --git a/public/images/site_content/pressPage/mediaVideo01Thumb.png b/public/images/site_content/pressPage/mediaVideo01Thumb.png new file mode 100644 index 0000000..dc728ca Binary files /dev/null and b/public/images/site_content/pressPage/mediaVideo01Thumb.png differ diff --git a/public/images/site_content/pressPage/mediaVideo02Thumb.png b/public/images/site_content/pressPage/mediaVideo02Thumb.png new file mode 100644 index 0000000..dea6bde Binary files /dev/null and b/public/images/site_content/pressPage/mediaVideo02Thumb.png differ diff --git a/public/images/site_content/pressPage/mediaVideo03Thumb.png b/public/images/site_content/pressPage/mediaVideo03Thumb.png new file mode 100644 index 0000000..dea6bde Binary files /dev/null and b/public/images/site_content/pressPage/mediaVideo03Thumb.png differ diff --git a/public/images/site_content/pressPage/mediaVideo04Thumb.png b/public/images/site_content/pressPage/mediaVideo04Thumb.png new file mode 100644 index 0000000..dea6bde Binary files /dev/null and b/public/images/site_content/pressPage/mediaVideo04Thumb.png differ diff --git a/public/images/site_content/pressRelease/_default.png b/public/images/site_content/pressRelease/_default.png new file mode 100644 index 0000000..62b31fb Binary files /dev/null and b/public/images/site_content/pressRelease/_default.png differ diff --git a/public/images/site_content/productsPage/appImg.png b/public/images/site_content/productsPage/appImg.png new file mode 100644 index 0000000..92fa5b9 Binary files /dev/null and b/public/images/site_content/productsPage/appImg.png differ diff --git a/public/images/site_content/productsPage/appImgLight.png b/public/images/site_content/productsPage/appImgLight.png new file mode 100644 index 0000000..db332ea Binary files /dev/null and b/public/images/site_content/productsPage/appImgLight.png differ diff --git a/public/images/site_content/productsPage/appMobileImg.jpg b/public/images/site_content/productsPage/appMobileImg.jpg new file mode 100644 index 0000000..1916fbf Binary files /dev/null and b/public/images/site_content/productsPage/appMobileImg.jpg differ diff --git a/public/images/site_content/productsPage/appMobileImgLight.jpg b/public/images/site_content/productsPage/appMobileImgLight.jpg new file mode 100644 index 0000000..44e45ca Binary files /dev/null and b/public/images/site_content/productsPage/appMobileImgLight.jpg differ diff --git a/public/images/site_content/productsPage/networkImg.jpg b/public/images/site_content/productsPage/networkImg.jpg new file mode 100644 index 0000000..ee72c79 Binary files /dev/null and b/public/images/site_content/productsPage/networkImg.jpg differ diff --git a/public/images/site_content/productsPage/networkImgLight.jpg b/public/images/site_content/productsPage/networkImgLight.jpg new file mode 100644 index 0000000..a04237c Binary files /dev/null and b/public/images/site_content/productsPage/networkImgLight.jpg differ diff --git a/public/images/site_content/productsPage/networkMobileImg.jpg b/public/images/site_content/productsPage/networkMobileImg.jpg new file mode 100644 index 0000000..0027068 Binary files /dev/null and b/public/images/site_content/productsPage/networkMobileImg.jpg differ diff --git a/public/images/site_content/productsPage/networkMobileImgLight.jpg b/public/images/site_content/productsPage/networkMobileImgLight.jpg new file mode 100644 index 0000000..b503646 Binary files /dev/null and b/public/images/site_content/productsPage/networkMobileImgLight.jpg differ diff --git a/public/images/site_content/productsPage/stackImage.png b/public/images/site_content/productsPage/stackImage.png new file mode 100644 index 0000000..bba214a Binary files /dev/null and b/public/images/site_content/productsPage/stackImage.png differ diff --git a/public/images/site_content/productsPage/stackImageLight.png b/public/images/site_content/productsPage/stackImageLight.png new file mode 100644 index 0000000..e16755b Binary files /dev/null and b/public/images/site_content/productsPage/stackImageLight.png differ diff --git a/public/images/site_content/productsPage/stackSvgImg.svg b/public/images/site_content/productsPage/stackSvgImg.svg new file mode 100644 index 0000000..4f5bb53 --- /dev/null +++ b/public/images/site_content/productsPage/stackSvgImg.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/site_content/productsPage/stackSvgImgLight.svg b/public/images/site_content/productsPage/stackSvgImgLight.svg new file mode 100644 index 0000000..b33b6b3 --- /dev/null +++ b/public/images/site_content/productsPage/stackSvgImgLight.svg @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/site_content/productsPage/tokenImg.jpg b/public/images/site_content/productsPage/tokenImg.jpg new file mode 100644 index 0000000..f2f7d54 Binary files /dev/null and b/public/images/site_content/productsPage/tokenImg.jpg differ diff --git a/public/images/site_content/productsPage/tokenImgLight.jpg b/public/images/site_content/productsPage/tokenImgLight.jpg new file mode 100644 index 0000000..6161750 Binary files /dev/null and b/public/images/site_content/productsPage/tokenImgLight.jpg differ diff --git a/public/images/site_content/productsPage/tokenMobileImg.jpg b/public/images/site_content/productsPage/tokenMobileImg.jpg new file mode 100644 index 0000000..cff9434 Binary files /dev/null and b/public/images/site_content/productsPage/tokenMobileImg.jpg differ diff --git a/public/images/site_content/productsPage/tokenMobileImgLight.jpg b/public/images/site_content/productsPage/tokenMobileImgLight.jpg new file mode 100644 index 0000000..8332b87 Binary files /dev/null and b/public/images/site_content/productsPage/tokenMobileImgLight.jpg differ diff --git a/public/images/site_content/productsPage/watchersImage.jpg b/public/images/site_content/productsPage/watchersImage.jpg new file mode 100644 index 0000000..2437a2a Binary files /dev/null and b/public/images/site_content/productsPage/watchersImage.jpg differ diff --git a/public/images/site_content/productsPage/watchersImageLight.jpg b/public/images/site_content/productsPage/watchersImageLight.jpg new file mode 100644 index 0000000..495169f Binary files /dev/null and b/public/images/site_content/productsPage/watchersImageLight.jpg differ diff --git a/public/images/site_content/productsPage/watchersMobileImage.jpg b/public/images/site_content/productsPage/watchersMobileImage.jpg new file mode 100644 index 0000000..7a7b9bd Binary files /dev/null and b/public/images/site_content/productsPage/watchersMobileImage.jpg differ diff --git a/public/images/site_content/productsPage/watchersMobileImageLight.jpg b/public/images/site_content/productsPage/watchersMobileImageLight.jpg new file mode 100644 index 0000000..3b16a0c Binary files /dev/null and b/public/images/site_content/productsPage/watchersMobileImageLight.jpg differ diff --git a/public/images/site_content/team/_default.png b/public/images/site_content/team/_default.png new file mode 100644 index 0000000..cdbfdd7 Binary files /dev/null and b/public/images/site_content/team/_default.png differ diff --git a/public/images/site_content/team/ashwin-phatak.jpg b/public/images/site_content/team/ashwin-phatak.jpg new file mode 100644 index 0000000..f135a00 Binary files /dev/null and b/public/images/site_content/team/ashwin-phatak.jpg differ diff --git a/public/images/site_content/team/erik-dies.jpg b/public/images/site_content/team/erik-dies.jpg new file mode 100644 index 0000000..28f681b Binary files /dev/null and b/public/images/site_content/team/erik-dies.jpg differ diff --git a/public/images/site_content/team/ian-norden.jpg b/public/images/site_content/team/ian-norden.jpg new file mode 100644 index 0000000..52208e8 Binary files /dev/null and b/public/images/site_content/team/ian-norden.jpg differ diff --git a/public/images/site_content/team/leah-light.jpg b/public/images/site_content/team/leah-light.jpg new file mode 100644 index 0000000..7ac3e63 Binary files /dev/null and b/public/images/site_content/team/leah-light.jpg differ diff --git a/public/images/site_content/team/rick-dudley.jpg b/public/images/site_content/team/rick-dudley.jpg new file mode 100644 index 0000000..2228844 Binary files /dev/null and b/public/images/site_content/team/rick-dudley.jpg differ diff --git a/public/images/site_content/team/seven-founding-members.png b/public/images/site_content/team/seven-founding-members.png new file mode 100644 index 0000000..cdbfdd7 Binary files /dev/null and b/public/images/site_content/team/seven-founding-members.png differ diff --git a/public/images/site_content/testimonial/_default.jpeg b/public/images/site_content/testimonial/_default.jpeg new file mode 100644 index 0000000..63e3b8e Binary files /dev/null and b/public/images/site_content/testimonial/_default.jpeg differ diff --git a/public/images/site_content/testimonial/cjremus.jpeg b/public/images/site_content/testimonial/cjremus.jpeg new file mode 100644 index 0000000..ddc4a28 Binary files /dev/null and b/public/images/site_content/testimonial/cjremus.jpeg differ diff --git a/public/images/site_content/testimonial/danfinlay.jpeg b/public/images/site_content/testimonial/danfinlay.jpeg new file mode 100644 index 0000000..63e3b8e Binary files /dev/null and b/public/images/site_content/testimonial/danfinlay.jpeg differ diff --git a/public/images/site_content/testimonial/dougiedeluca.jpeg b/public/images/site_content/testimonial/dougiedeluca.jpeg new file mode 100644 index 0000000..16a4dcb Binary files /dev/null and b/public/images/site_content/testimonial/dougiedeluca.jpeg differ diff --git a/public/images/site_content/testimonial/dystophiabreaker.jpeg b/public/images/site_content/testimonial/dystophiabreaker.jpeg new file mode 100644 index 0000000..b8ac49b Binary files /dev/null and b/public/images/site_content/testimonial/dystophiabreaker.jpeg differ diff --git a/public/images/site_content/testimonial/ether_gavin.jpeg b/public/images/site_content/testimonial/ether_gavin.jpeg new file mode 100644 index 0000000..117cc06 Binary files /dev/null and b/public/images/site_content/testimonial/ether_gavin.jpeg differ diff --git a/public/images/site_content/testimonial/kumavis.jpg b/public/images/site_content/testimonial/kumavis.jpg new file mode 100644 index 0000000..fbb2108 Binary files /dev/null and b/public/images/site_content/testimonial/kumavis.jpg differ diff --git a/public/images/site_content/testimonial/motherpredicate.jpeg b/public/images/site_content/testimonial/motherpredicate.jpeg new file mode 100644 index 0000000..87023ff Binary files /dev/null and b/public/images/site_content/testimonial/motherpredicate.jpeg differ diff --git a/public/images/site_content/testimonial/rickmanelius.jpg b/public/images/site_content/testimonial/rickmanelius.jpg new file mode 100644 index 0000000..53817ef Binary files /dev/null and b/public/images/site_content/testimonial/rickmanelius.jpg differ diff --git a/src/pages/press.tsx b/src/_old_pages/_press.tsx similarity index 74% rename from src/pages/press.tsx rename to src/_old_pages/_press.tsx index d454010..d6a58a9 100644 --- a/src/pages/press.tsx +++ b/src/_old_pages/_press.tsx @@ -1,7 +1,19 @@ +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + +/* import { getBlogPosts as serverGetBlogPosts, getBlogPostsCategories as serverGetBlogPostsCategories } from 'lib/blog' +*/ + +import { + getAllBlogPostsFromSource, + getAllBlogPostsCategoriesFromSource +} from 'lib/datocms-bypass' + +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== + import { InferGetStaticPropsType } from 'next' import { Meta } from '~/components/common/meta' @@ -19,11 +31,22 @@ import { import { request } from '../lib/datocms' export const getStaticProps = async () => { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + /* const [allBlogPosts, categories] = await Promise.all([ serverGetBlogPosts({ page: 1 }), serverGetBlogPostsCategories() ]) + */ + const [allBlogPosts, categories] = await Promise.all([ + getAllBlogPostsFromSource({ page: 1 }), + getAllBlogPostsCategoriesFromSource() + ]) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== + const heroBlogPost = allBlogPosts.data[0] const [ diff --git a/src/lib/datocms-bypass.ts b/src/lib/datocms-bypass.ts new file mode 100644 index 0000000..5d3694b --- /dev/null +++ b/src/lib/datocms-bypass.ts @@ -0,0 +1,794 @@ +import { promises as fs } from 'fs' +import { + getBlogPosts as serverGetBlogPosts, + getBlogPostsCategories as serverGetBlogPostsCategories +} from 'lib/blog' +import cms from 'lib/cms' +import path from 'path' + +//-----misc helpers +function datocmsDateToIng(date: string): number { + let dateInt = 0 + + date = date.replace(/\D/g, '') + dateInt = parseInt(date) + + if (Number.isInteger(dateInt) !== true) { + dateInt = 0 + } + + return dateInt +} + +//-----graphql query interception +export async function datocmsQueryIntercept(query: any) { + const parent = pluckFirstParentFromQuery(query) + const pageQueryWhitelist = getPageQueryBypassWhitelist() + const listingQueryWhitelist = getListingQueryDirectories() + let interceptData = {} + + if (listingQueryWhitelist.includes(parent)) { + interceptData = await datocmsListingQueryIntercept(query) + } else if (pageQueryWhitelist.includes(parent)) { + interceptData = await datocmsPageQueryIntercept(query) + } else { + interceptData = { + error: + 'Unable to intercept datocms query. No viable JSON alternative was defined.' + } + } + + return interceptData +} + +function getPageQueryBypassWhitelist(): string[] { + //-----stuff that has been (to the best of my knowledge) completley pulled over into JSON + //-----does not include iterative stuff like blog posts or events etc... + //-----this may only end up being scaffolding. + + const whitelist: string[] = [] + + whitelist.push('aboutPage') + whitelist.push('careersPage') + whitelist.push('communityPage') + whitelist.push('contactPage') + whitelist.push('homePage') + whitelist.push('partnersPage') + whitelist.push('pressPage') + whitelist.push('privacyPage') + whitelist.push('productsPage') + whitelist.push('termsPage') + + whitelist.push('header') + whitelist.push('footer') + + return whitelist +} + +function getListingQueryDirectories(): string[] { + //-----whitelist the listying types of content... + + const directories: string[] = [] + + directories.push('allEvents') + directories.push('allPositions') + directories.push('allPressReleases') + directories.push('allTeams') + directories.push('allTestimonials') + + return directories +} + +function pluckFirstParentFromQuery(query: any): string { + //-----only plucks the FIRST parent, if there are multiple parents in the query they will be ignored. + const parent: string = query + .replace(/[\W_]+/g, ' ') + .trim() + .split(' ')[0] + return parent +} + +async function datocmsPageQueryIntercept(query: any): Promise { + const parent: string = pluckFirstParentFromQuery(query) + const jsonDirectory: string = path.join(process.cwd(), 'json/site_content') + const jsonPath: string = jsonDirectory + '/' + parent + '.json' + let fileRawContents = '' + let fileJson: any = {} + let jsonData: any = {} + + try { + fileRawContents = await fs.readFile(jsonPath, 'utf8') + fileJson = JSON.parse(fileRawContents) + jsonData = fileJson.data + } catch (e) { + //console.log('Failed to parse JSON for file ' + jsonPath) + //console.log('JSON parse attempt resulted in the following error:') + //console.log(e) + } + + return jsonData +} + +async function datocmsListingQueryIntercept(query: any): Promise { + const parent: string = pluckFirstParentFromQuery(query) + let jsonData: any = {} + + switch (parent) { + case 'allBlogPosts': + //-----blog listing is handled in its own annoyingly special way + //-----could build it out here to be verbose but will not in the intrest of time/budget + //-----might need to add/remove some parent nodes in the JSON returned from getAllBlogPostsJson() to match the would-be expected format. + //jsonData = await getAllBlogPostsJson() + jsonData = {} + break + case 'allCategories': + //-----category listing is handled in its own annoyingly special way + //-----could build it out here to be verbose but will not in the intrest of time/budget + //-----might need to add/remove some parent nodes in the JSON returned from getAllBlogCategoriesJson() to match the would-be expected format. + //jsonData = await getAllBlogCategoriesJson() + jsonData = {} + break + case 'allEvents': + jsonData = await getAllEventsJson() + break + case 'allPositions': + jsonData = await getAllPositionsJson() + break + case 'allPressReleases': + jsonData = await getAllPressReleasesJson() + break + case 'allTeams': + jsonData = await getAllTeamsJson() + break + case 'allTestimonials': + jsonData = await getAllTestimonialsJson() + break + default: + jsonData = {} + break + } + + return jsonData +} + +//-----json traversal +function jsonFilePathIsValid(filePath: string) { + if (filePath.startsWith('_')) { + return false + } + + if (!filePath.endsWith('.json')) { + return false + } + + return true +} + +function jsonNodeExists(jsonData: any, node: string) { + if (node in jsonData === true) { + return true + } + + return false +} + +function jsonNodesExist(jsonData: any, nodes: any) { + //-----permit basic validation of the json we are trying to spit out by checking for existence of first order nodes + + let node = '' + let nodeGroup: any = [] + let nodeGroupNode = '' + let nodeGroupValid = false + + for (let i = 0; i < nodes.length; i++) { + node = nodes[i] + if (typeof node === 'string') { + //-----string means that PRIMARY node must exist... no checking for children etc... + if (jsonNodeExists(jsonData, node) === false) { + return false + } + } else if (Array.isArray(node)) { + //-----array means json is valid if ANY of the nodes in that array exist + nodeGroup = node + nodeGroupValid = false + for (let j = 0; j < nodeGroup.length; j++) { + nodeGroupNode = nodeGroup[j] + if (jsonNodeExists(jsonData, nodeGroupNode) === true) { + nodeGroupValid = true + } + } + if (nodeGroupValid === false) { + //-----none of the nodes in the group exist. + return false + } + } else { + //-----lets not get too crazy here. + return false + } + } + + return true +} + +export async function getJsonItemsFromDirectory( + jsonDirectory: string, + pluckerFunction: any, + validationNodes: any[] +): Promise { + const jsonFiles = await fs.readdir(jsonDirectory) + const returnJson: any[] = [] + + for (let i = 0; i < jsonFiles.length; i++) { + const jsonFile = jsonFiles[i] + if (jsonFilePathIsValid(jsonFile) === true) { + const jsonPath = path.join(jsonDirectory, jsonFile) + const fileRawContents = await fs.readFile(jsonPath, 'utf8') + let fileJsonContents = {} + let jsonParseSuccess = false + + try { + fileJsonContents = JSON.parse(fileRawContents) + jsonParseSuccess = true + } catch (e) { + jsonParseSuccess = false + //console.log('Failed to parse JSON for file ' + jsonPath) + //console.log('JSON parse attempt resulted in the following error:') + //console.log(e) + } + + if (jsonParseSuccess === true) { + fileJsonContents = JSON.parse(fileRawContents) + const jsonData = pluckerFunction(fileJsonContents) + jsonData.sourceFile = jsonFile + + if (jsonNodesExist(jsonData, validationNodes) === true) { + returnJson.push(jsonData) + } + } + } + } + + return returnJson +} + +//-----blogPosts +function getBlogJsonDirectoryPath() { + return path.join(process.cwd(), 'json/site_content/blogPost') +} + +function pluckBlogPostData(json: any) { + //let plucked: BlogPostRecord = {} + let plucked: any = {} + + if ('data' in json) { + if ('blogPost' in json.data) { + plucked = json.data.blogPost + } + } + + return plucked +} + +function getRequiedBlogPostNodes(): any[] { + const nodes = [] + const contentNodes = [] + + contentNodes.push('content') + contentNodes.push('htmlContent') + + //-----slug is now set per the JSON filename to avoid mismatches in routing + //nodes.push('slug') + nodes.push('date') + nodes.push('title') + nodes.push(contentNodes) + + return nodes +} + +function forceBlogPostsJsonSlugIntegrity(json: any[]) { + //-----this is used to force the blog post slug to match that of its parent file name. + + const returnJson: any[] = [] + + for (let i = 0; i < json.length; i++) { + const blogPost = json[i] + const sourceFile = blogPost.sourceFile + const newSlug = path.parse(sourceFile).name + blogPost.slug = newSlug + returnJson.push(blogPost) + } + + return returnJson +} + +function sortBlogPostsJsonByDate(json: any[]) { + const sortedJson: any[] = json + .slice() + .sort( + (a: any, b: any) => datocmsDateToIng(b.date) - datocmsDateToIng(a.date) + ) + + return sortedJson +} + +function getFakePaginationData(json: any) { + const pagination: any = {} + const totalPosts = json.length + + pagination.nextPage = null + pagination.page = 1 + pagination.step = totalPosts + pagination.total = totalPosts + pagination.totalPages = 1 + + return pagination +} + +async function getAllBlogPostsJson() { + const jsonDirectory = getBlogJsonDirectoryPath() + const validationNodes = getRequiedBlogPostNodes() + const blogPosts = await getJsonItemsFromDirectory( + jsonDirectory, + pluckBlogPostData, + validationNodes + ) + const pagination = getFakePaginationData(blogPosts) + const processedBlogPosts = forceBlogPostsJsonSlugIntegrity(blogPosts) + const sortedBlogPosts: any[] = sortBlogPostsJsonByDate(processedBlogPosts) + const allBlogPostsJson = { pagination: pagination, data: sortedBlogPosts } + + return allBlogPostsJson +} + +export async function getAllBlogPostsSlugsFromSource() { + const jsonDirectory = getBlogJsonDirectoryPath() + const jsonFiles = await fs.readdir(jsonDirectory) + const slugs = [] + + for (let i = 0; i < jsonFiles.length; i++) { + const jsonFile = jsonFiles[i] + if (jsonFilePathIsValid(jsonFile) === true) { + const slug = path.parse(jsonFile).name + const node = { slug: slug } + slugs.push(node) + } + } + + return slugs +} + +export async function getAllBlogPostsFromSource( + datocmsFilters: any +): Promise { + let allBlogPostsFromSource: any = {} + + if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === 'local_json') { + allBlogPostsFromSource = await getAllBlogPostsJson() + } else { + allBlogPostsFromSource = serverGetBlogPosts(datocmsFilters) + } + + return allBlogPostsFromSource +} + +async function getSingleBlogPostJsonBySlug(slug: string): Promise { + const jsonDirectory = getBlogJsonDirectoryPath() + const jsonFile = slug + '.json' + const jsonPath = path.join(jsonDirectory, jsonFile) + let blogPost: any = {} + + try { + const fileRawContents = await fs.readFile(jsonPath, 'utf8') + const fileJsonContents = JSON.parse(fileRawContents) + blogPost = pluckBlogPostData(fileJsonContents) + //-----continue to enforce standard set by forceBlogPostsJsonSlugIntegrity() + blogPost.slug = slug + } catch (e) { + //console.log('Failed to open or parse JSON for file ' + jsonPath) + //console.log('JSON parse attempt resulted in the following error:') + //console.log(e) + } + + return blogPost +} + +export async function getSingleBlogPostBySlugFromSource( + slug: string +): Promise { + let blogPostJson: any = {} + + if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === 'local_json') { + try { + blogPostJson = await getSingleBlogPostJsonBySlug(slug) + } catch (e) { + //console.log('Failed pull blog post with slug ' + slug) + //console.log('The attempt resulted in the following error:') + //console.log(e) + } + } else { + blogPostJson = await (await cms().SingleBlogPost({ slug })).blogPost + } + + return blogPostJson +} + +function getBlogPostCategorySlugs(blogPostJson: any) { + const categories = [] + + try { + const categoryNode = blogPostJson.category + for (let i = 0; i < categoryNode.length; i++) { + const category = categoryNode[i].slug + categories.push(category) + } + } catch (e) { + //console.log('Failed pull blog post categories') + //console.log('The attempt resulted in the following error:') + //console.log(e) + } + + return categories +} + +export async function getRelatedBlogPosts( + blogPostJson: any, + matchCount: number +): Promise { + const relatedBlogPosts = [] + const reservedSlugs = [] + let matchedCount = 0 + const matchCategories = getBlogPostCategorySlugs(blogPostJson) + const blogPosts = await getAllBlogPostsJson() + const blogPostsData = blogPosts.data + let candidateBlogPost: any = {} + let candidateBlogPostSlug = '' + const slug = blogPostJson.slug + + if (matchCategories.length > 0) { + reservedSlugs.push(slug) + for (let i = 0; i < blogPostsData.length; i++) { + candidateBlogPost = blogPostsData[i] + candidateBlogPostSlug = candidateBlogPost.slug + if (matchedCount >= matchCount) { + break + } + if (reservedSlugs.includes(candidateBlogPostSlug) === false) { + const candidateCategories = getBlogPostCategorySlugs(candidateBlogPost) + for (let j = 0; j < matchCategories.length; j++) { + const matchCategory = matchCategories[j] + if ( + candidateCategories.includes(matchCategory) === true && + reservedSlugs.includes(candidateBlogPostSlug) === false + ) { + relatedBlogPosts.push(candidateBlogPost) + reservedSlugs.push(candidateBlogPostSlug) + matchedCount++ + break + } + } + } + } + } + + if (matchedCount < matchCount) { + //-----if not enough matches, pull in anything thats left... + for (let i = 0; i < blogPostsData.length; i++) { + candidateBlogPost = blogPostsData[i] + candidateBlogPostSlug = candidateBlogPost.slug + if (matchedCount >= matchCount) { + break + } + if (reservedSlugs.includes(candidateBlogPostSlug) === false) { + relatedBlogPosts.push(candidateBlogPost) + reservedSlugs.push(candidateBlogPostSlug) + matchedCount++ + } + } + } + + return relatedBlogPosts +} + +//-----category +function getBlogCategoriesJsonDirectoryPath() { + return path.join(process.cwd(), 'json/site_content/category') +} + +function pluckBlogCategoriesData(json: any) { + let plucked = {} + + if ('data' in json) { + if ('category' in json.data) { + plucked = json.data.category + } + } + + return plucked +} + +function getRequiedBlogBlogCategoryNodes() { + const nodes = [] + + nodes.push('slug') + nodes.push('title') + + return nodes +} + +export async function getAllBlogCategoriesJson(): Promise { + const jsonDirectory = getBlogCategoriesJsonDirectoryPath() + const validationNodes = getRequiedBlogBlogCategoryNodes() + const allBlogCategoriesJson: any[] = await getJsonItemsFromDirectory( + jsonDirectory, + pluckBlogCategoriesData, + validationNodes + ) + + return allBlogCategoriesJson +} + +export async function getAllBlogPostsCategoriesFromSource(): Promise { + let allBlogPostsCategoriesFromSource: any[] = [] + + if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === 'local_json') { + allBlogPostsCategoriesFromSource = await getAllBlogCategoriesJson() + //let requestResults = await request(AllCategoriesQuery) + //allBlogPostsCategoriesFromSource = requestResults.allCategories + } else { + allBlogPostsCategoriesFromSource = await serverGetBlogPostsCategories() + } + + return allBlogPostsCategoriesFromSource +} + +//-----event +function getEventsJsonDirectoryPath() { + return path.join(process.cwd(), 'json/site_content/event') +} + +function pluckEventData(json: any) { + let plucked = {} + + if ('data' in json) { + if ('event' in json.data) { + plucked = json.data.event + } + } + + return plucked +} + +function getRequiedEventNodes() { + const nodes = [] + + nodes.push('title') + nodes.push('eventLocation') + nodes.push('eventStartdate') + nodes.push('eventEnddate') + + return nodes +} + +function sortEventsJsonByStartDate(json: any[]) { + const sortedJson: any[] = json + .slice() + .sort( + (a: any, b: any) => + datocmsDateToIng(b.eventStartdate) - datocmsDateToIng(a.eventStartdate) + ) + + return sortedJson +} + +async function getAllEventsJson(): Promise { + const jsonDirectory = getEventsJsonDirectoryPath() + const validationNodes = getRequiedEventNodes() + const events: any[] = await getJsonItemsFromDirectory( + jsonDirectory, + pluckEventData, + validationNodes + ) + const sortedEvents: any[] = sortEventsJsonByStartDate(events) + const allEventsJson = { allEvents: sortedEvents } + + return allEventsJson +} + +//-----position +function getPositionsJsonDirectoryPath() { + return path.join(process.cwd(), 'json/site_content/position') +} + +function pluckPositionData(json: any) { + let plucked = {} + + if ('data' in json) { + if ('position' in json.data) { + plucked = json.data.position + } + } + + return plucked +} + +function getRequiedPositionNodes() { + const nodes = [] + + nodes.push('id') + nodes.push('positionName') + nodes.push('positionLink') + + return nodes +} + +function sortPositionsJsonById(json: any[]) { + const sortedJson: any[] = json + .slice() + .sort((a: any, b: any) => parseInt(a.id) - parseInt(b.id)) + + return sortedJson +} + +async function getAllPositionsJson(): Promise { + const jsonDirectory = getPositionsJsonDirectoryPath() + const validationNodes = getRequiedPositionNodes() + const positions: any[] = await getJsonItemsFromDirectory( + jsonDirectory, + pluckPositionData, + validationNodes + ) + const sortedPositions: any[] = sortPositionsJsonById(positions) + const allPositionsJson = { allPositions: sortedPositions } + + return allPositionsJson +} + +//-----pressRelease +function getPressReleasesJsonDirectoryPath() { + return path.join(process.cwd(), 'json/site_content/pressRelease') +} + +function pluckPressReleaseData(json: any) { + let plucked = {} + + if ('data' in json) { + if ('pressRelease' in json.data) { + plucked = json.data.pressRelease + } + } + + return plucked +} + +function getRequiedPressReleaseNodes() { + const nodes = [] + + nodes.push('id') + nodes.push('title') + nodes.push('date') + nodes.push('link') + + return nodes +} + +function sortPressReleasesJsonByDate(json: any[]) { + const sortedJson: any[] = json + .slice() + .sort( + (a: any, b: any) => datocmsDateToIng(b.date) - datocmsDateToIng(a.date) + ) + + return sortedJson +} + +async function getAllPressReleasesJson(): Promise { + const jsonDirectory = getPressReleasesJsonDirectoryPath() + const validationNodes = getRequiedPressReleaseNodes() + const pressReleases: any[] = await getJsonItemsFromDirectory( + jsonDirectory, + pluckPressReleaseData, + validationNodes + ) + const sortedPressReleases: any[] = sortPressReleasesJsonByDate(pressReleases) + const allPressReleasesJson = { allPressReleases: sortedPressReleases } + + return allPressReleasesJson +} + +//-----team +function getTeamsJsonDirectoryPath() { + return path.join(process.cwd(), 'json/site_content/team') +} + +function pluckTeamData(json: any) { + let plucked = {} + + if ('data' in json) { + if ('team' in json.data) { + plucked = json.data.team + } + } + + return plucked +} + +function getRequiedTeamNodes() { + const nodes = [] + + nodes.push('id') + nodes.push('memberName') + + return nodes +} + +function sortTeamsJsonById(json: any[]) { + const sortedJson: any[] = json + .slice() + .sort((a: any, b: any) => parseInt(a.id) - parseInt(b.id)) + + return sortedJson +} + +async function getAllTeamsJson(): Promise { + const jsonDirectory = getTeamsJsonDirectoryPath() + const validationNodes = getRequiedTeamNodes() + const teams: any[] = await getJsonItemsFromDirectory( + jsonDirectory, + pluckTeamData, + validationNodes + ) + const sortedTeams: any[] = sortTeamsJsonById(teams) + const allTeamsJson = { allTeams: sortedTeams } + + return allTeamsJson +} + +//-----testimonial +function getTestimonialsJsonDirectoryPath() { + return path.join(process.cwd(), 'json/site_content/testimonial') +} + +function pluckTestimonialData(json: any) { + let plucked = {} + + if ('data' in json) { + if ('testimonial' in json.data) { + plucked = json.data.testimonial + } + } + + return plucked +} + +function getRequiedTestimonialNodes() { + const nodes = [] + + nodes.push('id') + nodes.push('testimonialImage') + nodes.push('testimonialText') + nodes.push('testimonialUsername') + + return nodes +} + +function sortTestimonialsJsonById(json: any[]) { + const sortedJson: any[] = json + .slice() + .sort((a: any, b: any) => parseInt(a.id) - parseInt(b.id)) + + return sortedJson +} + +async function getAllTestimonialsJson(): Promise { + const jsonDirectory = getTestimonialsJsonDirectoryPath() + const validationNodes = getRequiedTestimonialNodes() + const testimonials: any[] = await getJsonItemsFromDirectory( + jsonDirectory, + pluckTestimonialData, + validationNodes + ) + const sortedTestimonals: any[] = sortTestimonialsJsonById(testimonials) + const allTestimonialsJson = { allTestimonials: sortedTestimonals } + + return allTestimonialsJson +} diff --git a/src/lib/datocms.ts b/src/lib/datocms.ts index 1fdf304..d88d9a0 100644 --- a/src/lib/datocms.ts +++ b/src/lib/datocms.ts @@ -1,3 +1,6 @@ +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== +import { datocmsQueryIntercept } from 'lib/datocms-bypass' +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== import tiny from 'tiny-json-http' interface props { @@ -7,6 +10,9 @@ interface props { } export async function request({ query, variables, preview }: props) { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + /* let endpoint = 'https://graphql.datocms.com' if (preview) { @@ -30,4 +36,42 @@ export async function request({ query, variables, preview }: props) { } return body.data + */ + + if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === 'local_json') { + let interceptData: any = await datocmsQueryIntercept(query) + + if (interceptData.error) { + console.error(interceptData.error) + interceptData = {} + return interceptData + } else { + return interceptData + } + } else { + let endpoint = 'https://graphql.datocms.com' + + if (preview) { + endpoint += `/preview` + } + + const { body } = await tiny.post({ + url: endpoint, + headers: { + authorization: `Bearer ${process.env.NEXT_PUBLIC_CMS_ACCESS_TOKEN}` + }, + data: { + query, + variables + } + }) + + if (body.errors) { + console.error('The query has errors!') + throw body.errors + } + + return body.data + } + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== } diff --git a/src/pages/about.tsx b/src/pages/about.tsx index 0289512..144b1ef 100644 --- a/src/pages/about.tsx +++ b/src/pages/about.tsx @@ -1,7 +1,19 @@ +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + +/* import { getBlogPosts as serverGetBlogPosts, getBlogPostsCategories as serverGetBlogPostsCategories } from 'lib/blog' +*/ + +import { + getAllBlogPostsFromSource, + getAllBlogPostsCategoriesFromSource +} from 'lib/datocms-bypass' + +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== + import { InferGetStaticPropsType } from 'next' import { Meta } from '~/components/common/meta' @@ -24,10 +36,21 @@ import { HeaderQuery } from '~/lib/cms/queries/header' import { request } from '../lib/datocms' export const getStaticProps = async () => { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + /* const [allBlogPosts, categories] = await Promise.all([ serverGetBlogPosts({ page: 1 }), serverGetBlogPostsCategories() ]) + */ + + const [allBlogPosts, categories] = await Promise.all([ + getAllBlogPostsFromSource({ page: 1 }), + getAllBlogPostsCategoriesFromSource() + ]) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== const heroBlogPost = allBlogPosts.data[0] diff --git a/src/pages/blog/[slug].tsx b/src/pages/blog/[slug].tsx index f2411ff..c060ade 100644 --- a/src/pages/blog/[slug].tsx +++ b/src/pages/blog/[slug].tsx @@ -3,7 +3,14 @@ import { getAllBlogPostsSlugs, getBlogPosts as serverGetBlogPosts } from 'lib/blog' -import cms from 'lib/cms' +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== +import { + getAllBlogPostsSlugsFromSource, + getSingleBlogPostBySlugFromSource, + getRelatedBlogPosts +} from 'lib/datocms-bypass' +//import cms from 'lib/cms' +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== import { GetStaticPaths, GetStaticProps, @@ -49,11 +56,34 @@ const BlogPost = ({ } export const getStaticPaths: GetStaticPaths = async () => { - const posts = await getAllBlogPostsSlugs() + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + //const posts = await getAllBlogPostsSlugs() + + async function postsSlugsJsonFromSource(): Promise { + let postsSlugsJson: any = {} + if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === 'local_json') { + postsSlugsJson = await getAllBlogPostsSlugsFromSource() + } else { + postsSlugsJson = await getAllBlogPostsSlugs() + } + + return postsSlugsJson + } + + const posts = await postsSlugsJsonFromSource() + + /* const paths = posts?.map((post) => ({ params: { slug: post.slug as string } })) + */ + + const paths = posts?.map((post: any) => ({ + params: { slug: post.slug as string } + })) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== return { paths, fallback: 'blocking' } } @@ -63,17 +93,47 @@ export const getStaticProps: GetStaticProps = async ( ) => { try { const slug = ctx.params?.slug as string - const post = await (await cms().SingleBlogPost({ slug })).blogPost + + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + //const post = await (await cms().SingleBlogPost({ slug })).blogPost + const post: any = await getSingleBlogPostBySlugFromSource(slug) + + /* const allBlogPosts = await serverGetBlogPosts({ filterSlugs: [slug], step: 100 }) - + const relatedPosts = allBlogPosts.data.filter((p) => p.category.some((c) => post?.category.some((postCategory) => c.slug === postCategory.slug) ) ) + */ + + let relatedBlogPostsFromSource = [] + + if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === 'local_json') { + relatedBlogPostsFromSource = await getRelatedBlogPosts(post, 3) + } else { + const allBlogPosts = await serverGetBlogPosts({ + filterSlugs: [slug], + step: 100 + }) + + relatedBlogPostsFromSource = allBlogPosts.data.filter((p) => + p.category.some((c) => + post?.category.some( + (postCategory: any) => c.slug === postCategory.slug + ) + ) + ) + } + + const relatedPosts = await relatedBlogPostsFromSource + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== const [footerData, headerData] = await Promise.all([ request(FooterQuery), diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx index 66d07a6..011a244 100644 --- a/src/pages/blog/index.tsx +++ b/src/pages/blog/index.tsx @@ -1,9 +1,18 @@ import { PageLayout } from 'components/layout/page' +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== +/* import { getBlogPosts, getBlogPosts as serverGetBlogPosts, getBlogPostsCategories as serverGetBlogPostsCategories } from 'lib/blog' +*/ +import { getBlogPosts } from 'lib/blog' +import { + getAllBlogPostsFromSource, + getAllBlogPostsCategoriesFromSource +} from 'lib/datocms-bypass' +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== import { InferGetStaticPropsType } from 'next' import { useRouter } from 'next/router' import { Fragment, useCallback, useMemo } from 'react' @@ -35,114 +44,160 @@ const BlogIndexPage = ({ headerData, page: { heroBlogPost } }: InferGetStaticPropsType) => { - const router = useRouter() - const { c, s } = router.query - const activeCategory = useMemo( - () => getMatchingCategory(categories, c as string), - [c, categories] - ) + if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === 'local_json') { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + //-----remove all pagnation, search, sorting etc... as a lot of this was already broken and breaking further with the bypass + return ( + + + + + + {initialBlogPosts.data.map((post, postIdx) => ( + + ))} + + + + ) + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== + } else { + //===== \/ START ORIGINAL PRE NEXT_PUBLIC_DATOCMS_BYPASS CODE \/ =================================== + //-----everything in this ELSE block was the original code + //-----naturally the wrapping conditonal WAS not in the original code + //-----typically the pattern has been copy and comment out the orignial code at its current block level, then modify it as needed... + //-----however this was huge block, so easier to do this and explain the variation from the norm. + const router = useRouter() + const { c, s } = router.query + const activeCategory = useMemo( + () => getMatchingCategory(categories, c as string), + [c, categories] + ) - const handleSearch = useCallback( - (e: React.ChangeEvent) => { - makeQuery(router, { s: e.target.value || null }, { replace: true }) - }, - [router] - ) + const handleSearch = useCallback( + (e: React.ChangeEvent) => { + makeQuery(router, { s: e.target.value || null }, { replace: true }) + }, + [router] + ) - const { - data: queriedPosts, - fetchNextPage, - hasNextPage, - isLoading, - isFetching - } = useInfiniteQuery( - ['posts', router.locale, c, s], - ({ pageParam, queryKey }) => { - const matchingCat = getMatchingCategory(categories, queryKey[2] as string) + const { + data: queriedPosts, + fetchNextPage, + hasNextPage, + isLoading, + isFetching + } = useInfiniteQuery( + ['posts', router.locale, c, s], + ({ pageParam, queryKey }) => { + const matchingCat = getMatchingCategory( + categories, + queryKey[2] as string + ) + return clientGetBlogPosts({ + page: pageParam, + category: matchingCat?.id ?? undefined, + search: queryKey[3] as string + }) + }, + { + refetchOnWindowFocus: false, + initialData: !activeCategory + ? { + pageParams: [undefined], + pages: [initialBlogPosts] + } + : undefined, + getNextPageParam: (lastPage) => + lastPage.pagination.nextPage ?? undefined + } + ) - return clientGetBlogPosts({ - page: pageParam, - category: matchingCat?.id ?? undefined, - search: queryKey[3] as string - }) - }, - { - refetchOnWindowFocus: false, - initialData: !activeCategory - ? { - pageParams: [undefined], - pages: [initialBlogPosts] + let queriedPostsHasResults = false + + if ( + typeof queriedPosts !== 'undefined' && + 'pages' in queriedPosts === true + ) { + if (0 in queriedPosts.pages === true) { + if ('pagination' in queriedPosts.pages[0]) { + if ('total' in queriedPosts.pages[0].pagination) { + if (queriedPosts.pages[0].pagination.total > 0) { + queriedPostsHasResults = true + } } - : undefined, - getNextPageParam: (lastPage) => lastPage.pagination.nextPage ?? undefined + } + } } - ) - const hasResults = !!queriedPosts?.pages[0]?.data?.length + //const hasResults = !!queriedPosts?.pages[0]?.data?.length + const hasResults = queriedPostsHasResults - return ( - - - - - - - - - {!isLoading ? ( - hasResults ? ( - queriedPosts?.pages?.map((page, pageIdx) => ( - - {page.data.map((post, postIdx) => ( - - ))} - - )) + + + + {!isLoading ? ( + hasResults ? ( + queriedPosts?.pages?.map((page, pageIdx) => ( + + {page.data.map((post, postIdx) => ( + + ))} + + )) + ) : ( +

No results.

+ ) ) : ( -

No results.

- ) - ) : ( -

Loading...

+

Loading...

+ )} +
+ {hasNextPage && ( + + + )} -
- {hasNextPage && ( - - - - )} -
- ) + + ) + //===== /\ FINISH ORIGINAL PRE NEXT_PUBLIC_DATOCMS_BYPASS CODE /\ =================================== + } } const clientGetBlogPosts = async ({ @@ -179,10 +234,35 @@ const clientGetBlogPosts = async ({ } export const getStaticProps = async () => { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + function getInitialBlogPostsDataProp(allBlogPosts: any) { + let initialBlogPostsDataProp: any[] = [] + + if (process.env.NEXT_PUBLIC_DATOCMS_BYPASS_TYPE === 'local_json') { + //-----no pagination + initialBlogPostsDataProp = allBlogPosts.data + } else { + //-----old setup had pagination but it wasn't working... + initialBlogPostsDataProp = allBlogPosts.data.slice(0, 9) + } + + return initialBlogPostsDataProp + } + + /* const [allBlogPosts, categories] = await Promise.all([ serverGetBlogPosts({ page: 1 }), serverGetBlogPostsCategories() ]) + */ + + const [allBlogPosts, categories] = await Promise.all([ + getAllBlogPostsFromSource({ page: 1 }), + getAllBlogPostsCategoriesFromSource() + ]) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== const heroBlogPost = allBlogPosts.data[0] @@ -195,7 +275,12 @@ export const getStaticProps = async () => { props: { initialBlogPosts: { pagination: allBlogPosts.pagination, - data: allBlogPosts.data.slice(0, 9) + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + //data: allBlogPosts.data.slice(0, 9) + data: getInitialBlogPostsDataProp(allBlogPosts) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== }, footerData: footerData?.footer, headerData: headerData?.header, diff --git a/src/pages/careers.tsx b/src/pages/careers.tsx index 87be47c..d361f6b 100644 --- a/src/pages/careers.tsx +++ b/src/pages/careers.tsx @@ -1,7 +1,19 @@ +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + +/* import { getBlogPosts as serverGetBlogPosts, getBlogPostsCategories as serverGetBlogPostsCategories } from 'lib/blog' +*/ + +import { + getAllBlogPostsFromSource, + getAllBlogPostsCategoriesFromSource +} from 'lib/datocms-bypass' + +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== + import { InferGetStaticPropsType } from 'next' import { Meta } from '~/components/common/meta' @@ -24,10 +36,21 @@ import { HeaderQuery } from '~/lib/cms/queries/header' import { request } from '../lib/datocms' export const getStaticProps = async () => { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + /* const [allBlogPosts, categories] = await Promise.all([ serverGetBlogPosts({ page: 1 }), serverGetBlogPostsCategories() ]) + */ + + const [allBlogPosts, categories] = await Promise.all([ + getAllBlogPostsFromSource({ page: 1 }), + getAllBlogPostsCategoriesFromSource() + ]) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== const heroBlogPost = allBlogPosts.data[0] @@ -72,6 +95,7 @@ export const getStaticProps = async () => { } } + const Careers = ({ heroData, valuesData, diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 885a2b9..cd21ef4 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,7 +1,19 @@ +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + +/* import { getBlogPosts as serverGetBlogPosts, getBlogPostsCategories as serverGetBlogPostsCategories } from 'lib/blog' +*/ + +import { + getAllBlogPostsFromSource, + getAllBlogPostsCategoriesFromSource +} from 'lib/datocms-bypass' + +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== + import { InferGetStaticPropsType } from 'next' import { Meta } from '~/components/common/meta' @@ -23,10 +35,21 @@ import { import { request } from '../lib/datocms' export const getStaticProps = async () => { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + /* const [allBlogPosts, categories] = await Promise.all([ serverGetBlogPosts({ page: 1 }), serverGetBlogPostsCategories() ]) + */ + + const [allBlogPosts, categories] = await Promise.all([ + getAllBlogPostsFromSource({ page: 1 }), + getAllBlogPostsCategoriesFromSource() + ]) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== const heroBlogPost = allBlogPosts.data[0] diff --git a/src/pages/partners.tsx b/src/pages/partners.tsx index 2b9207d..e3a0a71 100644 --- a/src/pages/partners.tsx +++ b/src/pages/partners.tsx @@ -1,7 +1,19 @@ +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + +/* import { getBlogPosts as serverGetBlogPosts, getBlogPostsCategories as serverGetBlogPostsCategories } from 'lib/blog' +*/ + +import { + getAllBlogPostsFromSource, + getAllBlogPostsCategoriesFromSource +} from 'lib/datocms-bypass' + +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== + import { InferGetStaticPropsType } from 'next' import { Meta } from '~/components/common/meta' @@ -22,10 +34,21 @@ import { import { request } from '../lib/datocms' export const getStaticProps = async () => { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + /* const [allBlogPosts, categories] = await Promise.all([ serverGetBlogPosts({ page: 1 }), serverGetBlogPostsCategories() ]) + */ + + const [allBlogPosts, categories] = await Promise.all([ + getAllBlogPostsFromSource({ page: 1 }), + getAllBlogPostsCategoriesFromSource() + ]) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== const heroBlogPost = allBlogPosts.data[0] diff --git a/src/pages/products.tsx b/src/pages/products.tsx index 5c731df..9aba80e 100644 --- a/src/pages/products.tsx +++ b/src/pages/products.tsx @@ -1,7 +1,19 @@ +//===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + +/* import { getBlogPosts as serverGetBlogPosts, getBlogPostsCategories as serverGetBlogPostsCategories } from 'lib/blog' +*/ + +import { + getAllBlogPostsFromSource, + getAllBlogPostsCategoriesFromSource +} from 'lib/datocms-bypass' + +//===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== + import { InferGetStaticPropsType } from 'next' import { Meta } from '~/components/common/meta' @@ -27,10 +39,21 @@ import { import { request } from '../lib/datocms' export const getStaticProps = async () => { + //===== \/ START NEXT_PUBLIC_DATOCMS_BYPASS \/ ==================================================== + + /* const [allBlogPosts, categories] = await Promise.all([ serverGetBlogPosts({ page: 1 }), serverGetBlogPostsCategories() ]) + */ + + const [allBlogPosts, categories] = await Promise.all([ + getAllBlogPostsFromSource({ page: 1 }), + getAllBlogPostsCategoriesFromSource() + ]) + + //===== /\ FINISH NEXT_PUBLIC_DATOCMS_BYPASS /\ ==================================================== const heroBlogPost = allBlogPosts.data[0]