From 217760f2d3ad732cf682802f153a840e2920cc61 Mon Sep 17 00:00:00 2001 From: Roberto Lucas Date: Tue, 27 Aug 2024 11:34:23 -0600 Subject: [PATCH] chore: august 3rd release (#344) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: add @types/uuid * chore: add @types/uuid * fix(app-lib): tsconfig pkg extends * fix: missing encoding dep * feat: sitemap (#290) * feat: add sitemap * fix: error handling * fix: move common types * fix: update base url in sitemap * Update apps/webapp/app/(routes)/[lang]/blog/[category]/sitemap.ts Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update apps/webapp/app/(routes)/[lang]/wallet/sitemap.ts Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update apps/webapp/app/(routes)/[lang]/whitepaper/sitemap.ts Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * fix: build error * fix: remove unnecessary async declarative * vendor: add @types/uuid * fix: remove console log for debug * fix: add 'async' to all sitemaps * fix: make ProjectPageParams extends CommonPageParams --------- Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * fix(webapp): UI/UX tweaks and upts from final v1 landing designs (#291) * (webapp)fix:FAQ border radi + muted color * (webapp)fix:newsletter spacing * (webapp)fix:participate spacing and font family * (webapp)fix:header spacing + bitlauncher logo size * (webapp)fix:blog section 4 per row + purple link + gap * (webapp)fix:hero-card spacing * (webapp)fix:auction-card spaces * (webapp)fix:Upcoming grid gap * (webapp)fix:article card - text-left * (webapp)fix:media-card text-start * (webapp)fix:media-section purple link + space * (webapp)fix:participate gap + section font and padding * (webapp)fix:footer links padding + other spacing fix * (webapp)fix:upcoming background circle * (webapp)fix:footer margin-10 * (webapp)fix:upcoming bg shape * (webapp)-andler-changes-request * (webapp)-debug-toggle navigation icon * (webapp)-create-mobile-navigation-context * (webapp)-create-mobile-navigation-context * (webapp)-fix navigation bug - add key * (webapp)-Andler-changes * (webapp)-responsive - upcoming * (webapp)-fix-responsive-whychooseUs * (webapp)-fix-responsive-participate * (webapp)-fix-responsive-media * (webapp)-fix-responsive-articles * (webapp)-responsive-project-header+info+sharebtn * (webapp)-feat-about-base * (webapp)-feat-about-landing-complete * (webapp)-feat-security-updated * (webapp)-fix-steps-padding * fix typo * fix bgHeader type * fix bgHeader type * impr(webapp): layout tweaks * impr(webapp): info pages img asset + css class * fix: bun.lockb --------- Co-authored-by: Roberto Lucas * feat: multichain indexer ( part 1 ) (#293) * chore: update app-contracts * feat: multichain indexer * feat: prod chains * feat: presale wallet * feat: blpl token * feat: blpl token * chore: update bunlock * feat: blpl token * feat: presale evm contribs * feat: blpl token * wip: presale indexer * chore(indexer): fix dockerfile * feat: presale transaction indexing (#296) * feat: report transaction id * feat: save deposits data * feat: save deposits data * feat: read presale transactions * chore: disable view all * feat: update presale deposits ui * feat: display amount raised and contributors * chore: environment chains and tokens * fix(webapp): desktop padding (#297) * feat: multichain presale deposits (#298) * chore: environment chains and tokens * fix: chain switch * fix: chain switch * feat: update nav links (#299) * chore: environment chains and tokens * feat: update nav links * chore: renable swaps service, index token from latest block * feat(webapp): wallet ui updates (#300) * fix: wagmi config * feat(webapp): presale contribution report * feat: realtime presale data * feat: realtime presale data * feat: update dropdown menu * feat(supabase): update schema and types * chore: debug presale token issuance * feat: show issuance trx link on table * impr(webapp): upt mob ver of more info cards (#294) (#305) * styles: update mobile version for information cards of home page * styles: use tailwind css variables * styles: add font family futura pt * styles, config: add cornflowerblue variables on tailwind config and update information cards' background * config: update font importation * chore: replace BC with BL * chore: replace bc files with bl files * chore: replace bc files with bl files --------- Co-authored-by: Leandro Gavidia Santamaria <93232139+leandrogavidia@users.noreply.github.com> * chore: august 2nd release [at TEST] (#315) * impr(webapp): upt mob ver of more info cards (#294) * styles: update mobile version for information cards of home page * styles: use tailwind css variables * styles: add font family futura pt * styles, config: add cornflowerblue variables on tailwind config and update information cards' background * config: update font importation * chore: replace BC with BL * chore: replace bc files with bl files * chore: replace bc files with bl files --------- Co-authored-by: Roberto Lucas * fix(impr): verify font-style and text wording across landing (#309) * fix(impr): verify font-style across landing * fix(webapp): lufga bold font config * fix(webapp): investor wording to contributor on texts * fix(webapp): investors wording to contributors on texts (plural) * chore(webapp): text, invest wording upt to contribute * config: add multibase custom hook (#310) * config: add multibase custom hook * chore: update multibase hook and session hook * chore: change inverted params * impr(fix): multibase init call --------- Co-authored-by: Roberto Lucas * feat: referral section ui - ver 0.1a (#311) * chore(webapp): update gitignore * chore: update cursor agent rules * fix: action example on cursor rules * fix: action example on cursor rules * fix: action example on cursor rules * feat(webapp): use dynamic loading in homepage (#313) * feat: token issuance with trigger.dev and alchemy hooks - part 1 (#312) * feat: alchemy transfer hooks * feat(trigger): listen token transfers * feat(app-lib): add pino logger * feat(trigger): address activity handler * feat(indexer): security updates and alchemy proxy * fix(webapp): logger bug * chore(indexer): sentry middleware and app config * chore(indexer): cleanup * chore: update bunlock * docs: update readme Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --------- Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * feat(indexer): trigger address activity job (#316) * impr(webapp): check short link gen (#301) * fix(webapp): desktop padding * wip(imp): check user share link * fix(webapp): error var name scope * chore: updating supabase schema & types * feat(webapp): user table short link + session upsert * chore: use biome 🚀 (#317) * chore: use biome * chore: use biome * chore(indexer): verify call with alchemy signing key (#318) * devops(indexer): update dockerfile * chore(indexer): cleanup * chore: update gitignore * debug(indexer): gcloud deployment * debug(indexer): gcloud deployment * debug(indexer): gcloud deployment * debug(indexer): gcloud deployment * debug(indexer): gcloud deployment * fix(trigger): install right trigger.dev dep --------- Co-authored-by: Leandro Gavidia Santamaria <93232139+leandrogavidia@users.noreply.github.com> Co-authored-by: Gabo Esquivel Co-authored-by: Gabo Esquivel Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * fix: merge typo * fix: tailwind font config * chore: august 3rd release [pre-prod] (#334) * impr(webapp): upt mob ver of more info cards (#294) * styles: update mobile version for information cards of home page * styles: use tailwind css variables * styles: add font family futura pt * styles, config: add cornflowerblue variables on tailwind config and update information cards' background * config: update font importation * chore: replace BC with BL * chore: replace bc files with bl files * chore: replace bc files with bl files --------- Co-authored-by: Roberto Lucas * fix(impr): verify font-style and text wording across landing (#309) * fix(impr): verify font-style across landing * fix(webapp): lufga bold font config * fix(webapp): investor wording to contributor on texts * fix(webapp): investors wording to contributors on texts (plural) * chore(webapp): text, invest wording upt to contribute * config: add multibase custom hook (#310) * config: add multibase custom hook * chore: update multibase hook and session hook * chore: change inverted params * impr(fix): multibase init call --------- Co-authored-by: Roberto Lucas * feat: referral section ui - ver 0.1a (#311) * chore(webapp): update gitignore * chore: update cursor agent rules * fix: action example on cursor rules * fix: action example on cursor rules * fix: action example on cursor rules * feat(webapp): use dynamic loading in homepage (#313) * feat: token issuance with trigger.dev and alchemy hooks - part 1 (#312) * feat: alchemy transfer hooks * feat(trigger): listen token transfers * feat(app-lib): add pino logger * feat(trigger): address activity handler * feat(indexer): security updates and alchemy proxy * fix(webapp): logger bug * chore(indexer): sentry middleware and app config * chore(indexer): cleanup * chore: update bunlock * docs: update readme Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --------- Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * feat(indexer): trigger address activity job (#316) * impr(webapp): check short link gen (#301) * fix(webapp): desktop padding * wip(imp): check user share link * fix(webapp): error var name scope * chore: updating supabase schema & types * feat(webapp): user table short link + session upsert * chore: use biome 🚀 (#317) * chore: use biome * chore: use biome * chore(indexer): verify call with alchemy signing key (#318) * devops(indexer): update dockerfile * chore(indexer): cleanup * chore: update gitignore * debug(indexer): gcloud deployment * debug(indexer): gcloud deployment * debug(indexer): gcloud deployment * debug(indexer): gcloud deployment * debug(indexer): gcloud deployment * fix(trigger): install right trigger.dev dep * fix(indexer): trust proxy * feat: token issuance trigger part 2 (#321) * chore(alchemy): use gcloud endpoint * docs(alchemy): update readme file * docs(indexer): update readme file * debug(indexer): gcloud automated deployment * chore(alchemy): env values validation * chore(indexer): improve logging * chore(indexer): improve logging * chore(indexer): stringify all logs * debug(indexer): disable alchemy signature validation * debug(indexer): disable alchemy signature validation * chore(indexer): validate env and use trigger secret * debug(faucet): save dist/ empty folder in git * chore: update deps * chore: improve env validations * fix(alchemy): wrong import * chore(indexer): improve logging * debug(indexer): alchemy webhook handler * debug(indexer): disable helmet * debug(indexer): alchemy hook handler * debug(indexer): alchemy hook handler * debug(indexer): alchemy hook handler * debug(indexer): alchemy hook handler * feat(indexer): alchemy signature middleware * feat(indexer): validate alchemy signature * chore(indexer): security improvements (#323) * chore(indexer): disable alchemy validation * feat: presale token issuance part 3 (#325) * chore(alchemy): export types * chore(indexer): log process event id * chore(indexer): log process event id * chore: prod network conig, validate networks * wip(indexer): validate before triggering * chore: add try catch Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: clean up * devops(indexer): update dockerfile * chore: format code * devops: remove lhci reports * fix(indexer): wrong import * feat(trigger): transfer blpl token (#327) * feat(trigger): transfer blpl token * docs(trigger): token issuance jsdocs Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --------- Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * devops(indexer): update dockerfile * chore: remove type:module (#329) * chore: remove type:module * chore: remove type:module * devops(indexer): test deployment * devops(indexer): test deployment * chore(supabase): use module * feat(trigger): presale token issuance * chore: format * chore: use type:module for monorepo * impr(webapp): add multibase provider (#326) * config: create multibase provider * chore: update multibase env variable * impr(webapp): UI Update (#314) * (wepapp)impr:landing distribution + filenames * (wepapp)impr:auction card badge * (wepapp)impr:steps pading + new text-size * (wepapp)impr:text-size * (wepapp)impr:refactored about us section * (wepapp)impr:new tittles learning section * (wepapp)impr:security section * (wepapp)impr:tittle style and fonts * (wepapp)impr:responsive impr * fix:about section text-18 instead of text-sm * fix:security responsive * fix:hero padding * fix:Final Mobil Tweaks * impr(webapp): landing heros + mob-nav spacing * impr(webapp): landing learn section updated blog links * fix(webapp): info text foreground color * impr(webapp): landing responsiveness * imp(webapp): 3 steps needed responsiveness * chore(webapp): rm duplicate component --------- Co-authored-by: Roberto Lucas * feat: security updates (#331) * feat(indexer): validate right usdt,usdc address * feat(indexer): wait for finality and trx log * fix(indexer): wrong import * chore(indexer): fix build * chore(webapp): format * feat(webapp): bitlauncher blog category + blog tweaks (#324) * wip: static blog files upt * fix(webapp): blog article file gen + article ui tweaks * chore(webapp): app ver upt + cms gen script * docs: upt cms-ql.sh Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * impr(webapp): upt /services/datocms/datocms-blog.service.ts coderabbitai suggestion. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * impr(webapp): upt /services/datocms/datocms-blog.service.ts coderabbitai suggestion. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix(webapp): app build, missing file --------- Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: format files * fix(webapp): cached files error catch * impr(webapp): static blog file error catch * fix(webapp): static cached file chk * feat: supabase schema and webapp updates (#335) * feat(indexer): add address registration validation * feat: update database schema to support presales * feat: repo/tokens (#336) * chore: update repo packages (#338) * feat: stable coin selector * feat: presale ux ui * chore: repo/contracts pkg * chore: repo/utils pkg * chore: repo/tsconfig pkg * chore: repo/alchemy pkg * chore: repo/jobs pkg * chore: repo/supabase pkg * docs: update readme * chore: update bunlock * chore(indexer): alchemy webhook Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * docs: update readme Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * docs: update readme Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * chore(alchemy): invert ternary Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * chore: not nullable values, format code * chore: update node:crypto Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * feat(indexer): alchemy signature validation Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: fix build (#339) * feat: validate user registration on event proxy (#340) * chore: fix imports * feat(indexer): validate user is registered * feat: add presale deposit checks (#341) * feat(indexer): is registered address * feat(indexer): validate amount and timing * wip(webapp): update project, presale and auction pages * chore(webapp): improve error handling Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * feat: realtime presale data (#342) * feat: real time presale data * feat: real time presale data * fix(webapp): header margin (#343) * webapp:fix margin top * webapp:add header tag * webapp:fix margin top * webapp:add md top * webapp:add md top * webapp:fix project tittle + pills responsive --------- Co-authored-by: Leandro Gavidia Santamaria <93232139+leandrogavidia@users.noreply.github.com> Co-authored-by: Gabo Esquivel Co-authored-by: Gabo Esquivel Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> * chore(fix): update apps/trigger package.json dep * fix: cms gen merged files + use-session table upt + trigger/tsconfig upt --------- Co-authored-by: Gabo Esquivel Co-authored-by: Nathanael Liu Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: Brandon Fernández <31634868+Bran18@users.noreply.github.com> Co-authored-by: Leandro Gavidia Santamaria <93232139+leandrogavidia@users.noreply.github.com> Co-authored-by: Gabo Esquivel Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/lighthouse/budget.json | 27 - .github/workflows/lighthouse.yml | 85 - .vscode/settings.json | 4 +- README.md | 34 +- apps/faucet/.gitignore | 3 +- apps/faucet/package.json | 2 +- .../src/components/add-token-to-metamask.tsx | 2 +- apps/faucet/src/components/faucet-form.tsx | 2 +- apps/faucet/src/components/token-select.tsx | 2 +- apps/faucet/src/hooks/use-usdt-balance.ts | 2 +- apps/indexer/.env-sample | 4 +- apps/indexer/Dockerfile | 17 +- apps/indexer/README.md | 15 + apps/indexer/package.json | 12 +- apps/indexer/src/api/alchemy.ts | 150 + .../src/{routes => api}/healthcheck.ts | 0 apps/indexer/src/api/index.ts | 98 + apps/indexer/src/config.ts | 55 +- apps/indexer/src/index.ts | 6 +- apps/indexer/src/lib/supabase-client.ts | 54 +- .../src/modules/auction/auction-indexer.ts | 4 +- .../src/modules/auction/easyauction.ts | 4 +- .../src/modules/presale/evm-contributions.ts | 29 +- .../src/modules/presale/presale-issuer.ts | 2 +- apps/indexer/src/modules/swaps/cred-issuer.ts | 2 +- .../src/modules/swaps/evm-transfers.ts | 8 +- apps/indexer/tsconfig.json | 2 +- apps/trigger/package.json | 4 +- apps/trigger/tsconfig.json | 2 +- apps/webapp/actions.ts | 2 +- .../[lang]/[project]/auction/page.tsx | 8 +- .../app/(routes)/[lang]/[project]/page.tsx | 13 +- .../[lang]/[project]/presale/page.tsx | 13 +- .../[lang]/about/about-bitlauncher/page.tsx | 47 +- .../(routes)/[lang]/blog/[category]/page.tsx | 15 +- .../(routes)/[lang]/learn/security/page.tsx | 24 +- apps/webapp/app/(routes)/[lang]/page.tsx | 18 +- .../app/(routes)/[lang]/whitepaper/page.tsx | 18 +- apps/webapp/app/actions/save-deposit.ts | 32 +- apps/webapp/app/globals.css | 48 +- .../components/_wip/early-access-landing.tsx | 4 +- apps/webapp/components/_wip/feature-three.tsx | 10 +- apps/webapp/components/_wip/landing-page.tsx | 6 +- apps/webapp/components/_wip/landing-page2.tsx | 2 +- .../components/_wip/tokenization-landing.tsx | 6 +- apps/webapp/components/dialogs/register.tsx | 2 +- .../components/layout/footer/footer.tsx | 18 - .../layout/footer/learn-section.tsx | 137 - .../components/layout/footer/newsletter.tsx | 8 +- .../layout/header/bitcash-access.tsx | 5 +- .../webapp/components/layout/header/index.tsx | 18 +- .../components/layout/header/nav-links.tsx | 2 +- apps/webapp/components/layout/providers.tsx | 24 +- .../article-section.tsx} | 0 .../faq.tsx => section/faq-section.tsx} | 10 +- .../layout/section/learn-section.tsx | 119 + .../short-video-section.tsx} | 0 .../steps-section.tsx} | 10 +- .../session/register-dialog-content.tsx | 2 +- .../layout/session/session-button.tsx | 3 +- .../routes/about/about-bitlauncher/index.tsx | 47 +- .../components/routes/blog/article/index.tsx | 286 +- .../components/routes/blog/blog-sections.tsx | 8 +- .../components/routes/blog/category.tsx | 4 + .../routes/blog/hero-section/index.tsx | 2 +- .../components/routes/home/auction-card.tsx | 13 +- .../components/routes/home/features.tsx | 2 +- .../components/routes/home/hero/index.tsx | 10 +- .../routes/home/motion-figcaption.tsx | 38 +- .../components/routes/home/why-choose-us.tsx | 24 +- .../routes/project/auction/auction-bids.tsx | 4 +- .../routes/project/auction/auction-orders.tsx | 4 +- .../routes/project/copy-shorlink.tsx | 35 +- .../project/presale/presale-deposit-card.tsx | 39 +- .../presale/presale-transactions-card.tsx | 124 +- .../routes/project/project-header.tsx | 2 +- .../routes/project/project-pills.tsx | 2 +- .../routes/project/project-presale-data.tsx | 83 +- .../routes/project/register-address-form.tsx | 2 +- .../routes/wallet/balances-table.tsx | 2 +- .../components/routes/wallet/deposit-card.tsx | 2 +- .../routes/wallet/tabs/transactions-table.tsx | 2 +- .../routes/wallet/withdraw-card.tsx | 2 +- .../webapp/components/shared/article-card.tsx | 114 +- apps/webapp/components/shared/banner.tsx | 55 +- apps/webapp/components/shared/bg-header.tsx | 37 +- apps/webapp/components/shared/content.tsx | 2 +- apps/webapp/components/shared/countdown.tsx | 28 +- apps/webapp/components/shared/media-card.tsx | 47 +- .../components/shared/media-sections.tsx | 8 +- apps/webapp/components/shared/section.tsx | 4 +- apps/webapp/components/ui/dropdown-menu.tsx | 5 +- .../blog/bitlauncher/bitlauncher-index.json | 312 ++ ...launcher-presale-starting-august-31st.json | 569 ++++ .../what-is-a-public-token-presale.json | 526 +++ .../what-is-the-bitlauncher-bl-token.json | 342 ++ .../dictionaries/en/blog/blog-index.json | 91 +- .../dictionaries/es/blog/blog-index.json | 1004 ++++++ apps/webapp/hooks/use-auction-data.ts | 2 +- apps/webapp/hooks/use-balance.ts | 2 +- apps/webapp/hooks/use-multibase.ts | 18 +- apps/webapp/hooks/use-place-order.ts | 2 +- apps/webapp/hooks/use-session.tsx | 24 +- apps/webapp/lib/config.ts | 2 +- apps/webapp/lib/projects.ts | 4 +- apps/webapp/package.json | 18 +- apps/webapp/scripts/cms-ql.sh | 13 + apps/webapp/scripts/translate.ts | 4 +- .../datocms/datacms-blog-category.service.ts | 62 +- .../datocms/datocms-all-articles.service.ts | 1 + .../services/datocms/datocms-blog.service.ts | 183 +- .../graphql/generated/cms/runtime/batcher.ts | 49 +- .../graphql/generated/cms/runtime/fetcher.ts | 1 + .../graphql/generated/cms/schema.graphql | 467 ++- .../datocms/graphql/generated/cms/schema.ts | 560 +++- .../datocms/graphql/generated/cms/types.ts | 2909 +++++++++-------- apps/webapp/services/supabase/middleware.ts | 2 - apps/webapp/services/supabase/service.ts | 55 + apps/webapp/tailwind.config.js | 3 +- bun.lockb | Bin 823696 -> 836344 bytes package.json | 1 + packages/alchemy/.gitignore | 175 + packages/alchemy/README.md | 38 + packages/alchemy/package.json | 20 + packages/alchemy/src/config.ts | 22 + packages/alchemy/src/create.ts | 30 + packages/alchemy/src/index.ts | 1 + packages/alchemy/src/types.ts | 46 + packages/alchemy/tsconfig.json | 16 + packages/app-env/package.json | 4 +- packages/app-env/src/chains.ts | 35 +- packages/app-env/src/env.ts | 2 +- packages/contracts/README.md | 15 + .../{app-contracts => contracts}/package.json | 7 +- .../src/dev/auction/testnet-allow-list.ts | 0 .../src/dev/auction/testnet-deposit-order.ts | 0 .../src/dev/auction/testnet-easy-auction.ts | 0 .../src/dev/index.ts | 0 .../src/dev/tokens/eos-fake-bitusd.ts | 0 .../src/dev/tokens/eos-fake-usdt.ts | 0 .../src/dev/tokens/sepolia-usdt.ts | 0 .../src/dev/tokens/testnet-blpl.ts | 0 .../src/dev/tokens/testnet-mbots-prelaunch.ts | 0 .../src/dev/tokens/testnet-usd-cred.ts | 0 .../src/dev/tokens/testnet-usdt.ts | 0 .../{app-contracts => contracts}/src/index.ts | 0 .../src/prod/index.ts | 0 .../src/prod/tokens/eos-bitusd.ts | 0 .../src/prod/tokens/eos-usdt.ts | 0 .../src/prod/tokens/usdc.ts | 0 .../src/prod/tokens/usdt.ts | 0 .../{app-contracts => contracts}/src/types.ts | 2 + .../tsconfig.json | 0 packages/jobs/.env-sample | 5 + packages/jobs/.gitignore | 1 + packages/jobs/README.md | 23 + packages/jobs/package.json | 17 + packages/jobs/src/config.ts | 33 + packages/jobs/src/index.ts | 1 + packages/jobs/src/lib/presale-issuer.ts | 44 + packages/jobs/src/tmp/chains.ts | 60 + packages/jobs/src/tmp/index.ts | 2 + packages/jobs/src/trigger/activity.ts | 35 + packages/jobs/src/trigger/example.ts | 14 + packages/jobs/trigger.config.ts | 16 + packages/jobs/tsconfig.json | 16 + {apps => packages}/supabase/.gitignore | 0 {apps => packages}/supabase/README.md | 0 {apps => packages}/supabase/config.toml | 0 .../20240414235435_remote_schema.sql | 0 .../20240417041423_remote_schema.sql | 0 .../20240417153529_remote_schema.sql | 0 .../20240418165957_remote_schema.sql | 0 .../20240418231216_remote_schema.sql | 0 .../20240419001509_remote_schema.sql | 0 .../20240419003028_remote_schema.sql | 0 .../20240419010728_remote_schema.sql | 0 .../20240719192750_remote_schema.sql | 0 .../20240720155732_remote_schema.sql | 0 .../20240805231931_remote_schema.sql | 0 .../20240824213342_remote_schema.sql | 274 ++ .../20240825040214_remote_schema.sql | 33 + .../20240825181232_remote_schema.sql | 5 + {apps => packages}/supabase/package.json | 3 +- .../supabase/scripts/fake-orders.ts | 0 {apps => packages}/supabase/seed.sql | 0 {apps => packages}/supabase/src/index.ts | 0 .../supabase/src/supa.schemas.ts | 333 +- {apps => packages}/supabase/src/supa.types.ts | 289 +- packages/tokens/README.md | 15 + packages/tokens/package.json | 10 + packages/tokens/src/eos.ts | 22 + packages/tokens/src/evm.ts | 96 + packages/tokens/src/index.ts | 15 + packages/tokens/src/solana.ts | 21 + packages/tokens/src/types.ts | 34 + packages/tokens/tsconfig.json | 21 + .../{config-typescript => tsconfig}/base.json | 0 .../nextjs-14.json | 0 .../nextjs.json | 0 .../node16.json | 0 .../package.json | 2 +- .../react-library.json | 0 .../{config-typescript => tsconfig}/vite.json | 2 +- .../vite.node.json | 0 packages/{app-lib => utils}/package.json | 4 +- .../src/crypto/crypto.lib.ts | 0 .../{app-lib => utils}/src/crypto/index.ts | 0 .../{app-lib => utils}/src/date/date.lib.ts | 0 packages/{app-lib => utils}/src/date/index.ts | 0 .../{app-lib => utils}/src/error/error.lib.ts | 0 .../{app-lib => utils}/src/error/index.ts | 0 .../{app-lib => utils}/src/evm/evm.lib.ts | 0 packages/{app-lib => utils}/src/evm/index.ts | 0 packages/{app-lib => utils}/src/index.ts | 0 .../{app-lib => utils}/src/number/index.ts | 0 .../src/number/number.lib.ts | 0 .../{app-lib => utils}/src/object/index.ts | 0 .../src/object/object.lib.ts | 0 .../{app-lib => utils}/src/runtime/index.ts | 0 .../src/runtime/runtime.lib.ts | 0 .../{app-lib => utils}/src/string/index.ts | 0 .../src/string/string.lib.ts | 0 packages/{app-lib => utils}/src/url/index.ts | 0 .../{app-lib => utils}/src/url/url.lib.ts | 0 packages/{app-lib => utils}/tsconfig.json | 2 +- 226 files changed, 8658 insertions(+), 2721 deletions(-) delete mode 100644 .github/lighthouse/budget.json delete mode 100644 .github/workflows/lighthouse.yml create mode 100644 apps/indexer/src/api/alchemy.ts rename apps/indexer/src/{routes => api}/healthcheck.ts (100%) create mode 100644 apps/indexer/src/api/index.ts delete mode 100644 apps/webapp/components/layout/footer/learn-section.tsx rename apps/webapp/components/layout/{footer/recent.tsx => section/article-section.tsx} (100%) rename apps/webapp/components/layout/{footer/faq.tsx => section/faq-section.tsx} (69%) create mode 100644 apps/webapp/components/layout/section/learn-section.tsx rename apps/webapp/components/layout/{footer/short-video.tsx => section/short-video-section.tsx} (100%) rename apps/webapp/components/layout/{footer/participate.tsx => section/steps-section.tsx} (79%) create mode 100755 apps/webapp/dictionaries/en/blog/bitlauncher/bitlauncher-index.json create mode 100644 apps/webapp/dictionaries/en/blog/bitlauncher/welcome-to-the-bitlauncher-presale-starting-august-31st.json create mode 100644 apps/webapp/dictionaries/en/blog/bitlauncher/what-is-a-public-token-presale.json create mode 100644 apps/webapp/dictionaries/en/blog/bitlauncher/what-is-the-bitlauncher-bl-token.json create mode 100644 apps/webapp/dictionaries/es/blog/blog-index.json create mode 100755 apps/webapp/scripts/cms-ql.sh create mode 100644 apps/webapp/services/supabase/service.ts create mode 100644 packages/alchemy/.gitignore create mode 100644 packages/alchemy/README.md create mode 100644 packages/alchemy/package.json create mode 100644 packages/alchemy/src/config.ts create mode 100644 packages/alchemy/src/create.ts create mode 100644 packages/alchemy/src/index.ts create mode 100644 packages/alchemy/src/types.ts create mode 100644 packages/alchemy/tsconfig.json create mode 100644 packages/contracts/README.md rename packages/{app-contracts => contracts}/package.json (70%) rename packages/{app-contracts => contracts}/src/dev/auction/testnet-allow-list.ts (100%) rename packages/{app-contracts => contracts}/src/dev/auction/testnet-deposit-order.ts (100%) rename packages/{app-contracts => contracts}/src/dev/auction/testnet-easy-auction.ts (100%) rename packages/{app-contracts => contracts}/src/dev/index.ts (100%) rename packages/{app-contracts => contracts}/src/dev/tokens/eos-fake-bitusd.ts (100%) rename packages/{app-contracts => contracts}/src/dev/tokens/eos-fake-usdt.ts (100%) rename packages/{app-contracts => contracts}/src/dev/tokens/sepolia-usdt.ts (100%) rename packages/{app-contracts => contracts}/src/dev/tokens/testnet-blpl.ts (100%) rename packages/{app-contracts => contracts}/src/dev/tokens/testnet-mbots-prelaunch.ts (100%) rename packages/{app-contracts => contracts}/src/dev/tokens/testnet-usd-cred.ts (100%) rename packages/{app-contracts => contracts}/src/dev/tokens/testnet-usdt.ts (100%) rename packages/{app-contracts => contracts}/src/index.ts (100%) rename packages/{app-contracts => contracts}/src/prod/index.ts (100%) rename packages/{app-contracts => contracts}/src/prod/tokens/eos-bitusd.ts (100%) rename packages/{app-contracts => contracts}/src/prod/tokens/eos-usdt.ts (100%) rename packages/{app-contracts => contracts}/src/prod/tokens/usdc.ts (100%) rename packages/{app-contracts => contracts}/src/prod/tokens/usdt.ts (100%) rename packages/{app-contracts => contracts}/src/types.ts (99%) rename packages/{app-contracts => contracts}/tsconfig.json (100%) create mode 100644 packages/jobs/.env-sample create mode 100644 packages/jobs/.gitignore create mode 100644 packages/jobs/README.md create mode 100644 packages/jobs/package.json create mode 100644 packages/jobs/src/config.ts create mode 100644 packages/jobs/src/index.ts create mode 100644 packages/jobs/src/lib/presale-issuer.ts create mode 100644 packages/jobs/src/tmp/chains.ts create mode 100644 packages/jobs/src/tmp/index.ts create mode 100644 packages/jobs/src/trigger/activity.ts create mode 100644 packages/jobs/src/trigger/example.ts create mode 100644 packages/jobs/trigger.config.ts create mode 100644 packages/jobs/tsconfig.json rename {apps => packages}/supabase/.gitignore (100%) rename {apps => packages}/supabase/README.md (100%) rename {apps => packages}/supabase/config.toml (100%) rename {apps => packages}/supabase/migrations/20240414235435_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240417041423_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240417153529_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240418165957_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240418231216_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240419001509_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240419003028_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240419010728_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240719192750_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240720155732_remote_schema.sql (100%) rename {apps => packages}/supabase/migrations/20240805231931_remote_schema.sql (100%) create mode 100644 packages/supabase/migrations/20240824213342_remote_schema.sql create mode 100644 packages/supabase/migrations/20240825040214_remote_schema.sql create mode 100644 packages/supabase/migrations/20240825181232_remote_schema.sql rename {apps => packages}/supabase/package.json (92%) rename {apps => packages}/supabase/scripts/fake-orders.ts (100%) rename {apps => packages}/supabase/seed.sql (100%) rename {apps => packages}/supabase/src/index.ts (100%) rename {apps => packages}/supabase/src/supa.schemas.ts (61%) rename {apps => packages}/supabase/src/supa.types.ts (62%) create mode 100644 packages/tokens/README.md create mode 100644 packages/tokens/package.json create mode 100644 packages/tokens/src/eos.ts create mode 100644 packages/tokens/src/evm.ts create mode 100644 packages/tokens/src/index.ts create mode 100644 packages/tokens/src/solana.ts create mode 100644 packages/tokens/src/types.ts create mode 100644 packages/tokens/tsconfig.json rename packages/{config-typescript => tsconfig}/base.json (100%) rename packages/{config-typescript => tsconfig}/nextjs-14.json (100%) rename packages/{config-typescript => tsconfig}/nextjs.json (100%) rename packages/{config-typescript => tsconfig}/node16.json (100%) rename packages/{config-typescript => tsconfig}/package.json (75%) rename packages/{config-typescript => tsconfig}/react-library.json (100%) rename packages/{config-typescript => tsconfig}/vite.json (93%) rename packages/{config-typescript => tsconfig}/vite.node.json (100%) rename packages/{app-lib => utils}/package.json (85%) rename packages/{app-lib => utils}/src/crypto/crypto.lib.ts (100%) rename packages/{app-lib => utils}/src/crypto/index.ts (100%) rename packages/{app-lib => utils}/src/date/date.lib.ts (100%) rename packages/{app-lib => utils}/src/date/index.ts (100%) rename packages/{app-lib => utils}/src/error/error.lib.ts (100%) rename packages/{app-lib => utils}/src/error/index.ts (100%) rename packages/{app-lib => utils}/src/evm/evm.lib.ts (100%) rename packages/{app-lib => utils}/src/evm/index.ts (100%) rename packages/{app-lib => utils}/src/index.ts (100%) rename packages/{app-lib => utils}/src/number/index.ts (100%) rename packages/{app-lib => utils}/src/number/number.lib.ts (100%) rename packages/{app-lib => utils}/src/object/index.ts (100%) rename packages/{app-lib => utils}/src/object/object.lib.ts (100%) rename packages/{app-lib => utils}/src/runtime/index.ts (100%) rename packages/{app-lib => utils}/src/runtime/runtime.lib.ts (100%) rename packages/{app-lib => utils}/src/string/index.ts (100%) rename packages/{app-lib => utils}/src/string/string.lib.ts (100%) rename packages/{app-lib => utils}/src/url/index.ts (100%) rename packages/{app-lib => utils}/src/url/url.lib.ts (100%) rename packages/{app-lib => utils}/tsconfig.json (61%) diff --git a/.github/lighthouse/budget.json b/.github/lighthouse/budget.json deleted file mode 100644 index 2a9ae723f..000000000 --- a/.github/lighthouse/budget.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "path": "/*", - "timings": [ - { - "metric": "interactive", - "budget": 3000 - }, - { - "metric": "first-contentful-paint", - "budget": 1800 - } - ], - "resourceSizes": [ - { - "resourceType": "script", - "budget": 100 - } - ], - "resourceCounts": [ - { - "resourceType": "third-party", - "budget": 4 - } - ] - } -] diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml deleted file mode 100644 index b82adc759..000000000 --- a/.github/workflows/lighthouse.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Vercel Preview URL Lighthouse Audit - -on: - pull_request - -jobs: - generate_lighthouse_audit: - timeout-minutes: 30 - runs-on: ubuntu-latest - steps: - # - name: Add comment to PR - # id: loading_comment_to_pr - # uses: marocchino/sticky-pull-request-comment@v2.9.0 - # with: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # number: ${{ github.event.issue.number }} - # header: lighthouse - # message: | - # Running Lighthouse audit... - - name: Wait for 5 minutes - run: sleep 300 - - name: Capture Vercel preview URL - id: vercel_preview_url - uses: zentered/vercel-preview-url@v1.1.9 - env: - VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} - with: - vercel_project_id: prj_Bf0q4Q5mAwPh404L36RsGFOB84Qu - vercel_team_id: team_7PAgeqjbXkY6qdxNIEKLbVSC - - name: Get URL - run: echo "https://${{ steps.vercel_preview_url.outputs.preview_url }}" - - uses: actions/checkout@v4 - - name: Audit preview URL with Lighthouse - id: lighthouse_audit - uses: treosh/lighthouse-ci-action@11.4.0 - with: - urls: | - "https://${{ steps.vercel_preview_url.outputs.preview_url }}" - "https://${{ steps.vercel_preview_url.outputs.preview_url }}/about" - "https://${{ steps.vercel_preview_url.outputs.preview_url }}/security" - "https://${{ steps.vercel_preview_url.outputs.preview_url }}/bitcash-bitlauncher" - "https://${{ steps.vercel_preview_url.outputs.preview_url }}/blog" - "https://${{ steps.vercel_preview_url.outputs.preview_url }}/blog/ai" - # budgetPath: '.github/lighthouse/budget.json' - uploadArtifacts: true - temporaryPublicStorage: true - - name: Format lighthouse score - id: format_lighthouse_score - uses: actions/github-script@v7.0.1 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const manifest = ${{ steps.lighthouse_audit.outputs.manifest }}; - const links = ${{ steps.lighthouse_audit.outputs.links }}; - - const formatResult = (res) => Math.round((res * 100)); - const score = res => res >= 90 ? '🟢' : res >= 50 ? '🟠' : '🔴'; - - let comment = '⚡️ Lighthouse report\n' - - manifest.forEach(result => { - const pageLink = result.url; - comment += `\nPage: ${pageLink}\n`; - comment += `Report ${links[pageLink]}\n`; - comment += '| Category | Score |\n'; - comment += '| --- | --- |\n'; - - Object.keys(result.summary).forEach(key => result.summary[key] = formatResult(result.summary[key])); - comment += `| ${score(result.summary.performance)} Performance | ${result.summary.performance} |\n`; - comment += `| ${score(result.summary.accessibility)} Accessibility | ${result.summary.accessibility} |\n`; - comment += `| ${score(result.summary['best-practices'])} Best practices | ${result.summary['best-practices']} |\n`; - comment += `| ${score(result.summary.seo)} SEO | ${result.summary.seo} |\n`; - comment += `| ${score(result.summary.pwa)} PWA | ${result.summary.pwa} |\n`; - }); - - core.setOutput("comment", comment); - - name: Add comment to PR - id: comment_to_pr - uses: marocchino/sticky-pull-request-comment@v2.9.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - number: ${{ github.event.issue.number }} - header: lighthouse - message: | - ${{ steps.format_lighthouse_score.outputs.comment }} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index fa9f49187..6c70aba17 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,5 +15,7 @@ }, "editor.codeActionsOnSave": { "source.organizeImports.biome": "explicit" - } + }, + "editor.insertSpaces": true, + "editor.tabSize": 2 } diff --git a/README.md b/README.md index 57e9d3fcd..1f0756e76 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,6 @@ The Faucet application serves as a utility for distributing test tokens or curre The Indexer application is responsible for indexing blockchain data. It listens to the blockchain network, extracts relevant data from blocks, transactions, and events, and stores it in a structured format for easy querying and analysis. -#### Supabase (`/apps/supabase`) - -This application integrates with Supabase, a scalable and open-source Firebase alternative, providing real-time database functionality, authentication, storage, and more. It's designed to leverage Supabase services for backend functionalities. - -For more database schema details, refer to the [Database Schema](/apps/supabase/README.md) diagram. - #### Webapp (`/apps/webapp`) The Webapp is a front-end application that provides a user interface for interacting with the project's services. It includes features such as displaying blockchain data, interacting with smart contracts, and utilizing the Faucet for test tokens. @@ -55,25 +49,33 @@ Please refer to the [webapp README.md](/apps/webapp/README.md) for more details ### Packages -#### Config TypeScript (`/packages/config-typescript`) +#### Alchemy (`/packages/alchemy`) + +This package integrates with the Alchemy SDK, providing a robust interface for interacting with Ethereum and other blockchain networks. It includes utilities for creating and managing blockchain interactions, leveraging Alchemy's powerful API services. + +#### Config TypeScript (`/packages/tsconfig`) -This package offers a shared TypeScript configuration to standardize TypeScript compilation options across the project. +This package offers shared TypeScript configurations to standardize TypeScript compilation options across the project. It includes specific configurations for different environments and Node.js versions. -#### Smartsale Contracts (`/packages/app-contracts`) +#### Jobs (`/packages/jobs`) -Contains smart contracts for the Smartsale platform, including auction contracts, ERC20 tokens, and other blockchain-based contracts. These are essential for the project's blockchain functionalities. +The Jobs package contains background tasks and scheduled processes for the SmartSale platform. It utilizes Trigger.dev for managing and deploying these jobs, ensuring efficient handling of asynchronous operations and scheduled tasks. -#### Smartsale Env (`/packages/app-env`) +#### Supabase (`/packages/supabase`) -Provides environment configurations and utilities for the Smartsale platform, ensuring that different environments (development, testing, production) can be managed and configured efficiently. +This package integrates with Supabase, providing real-time database functionality, authentication, and storage services. It includes type generation and schema validation tools to ensure type safety when interacting with the Supabase backend. -#### Smartsale Lib (`/packages/app-lib`) +#### Tokens (`/packages/tokens`) -A library of reusable code for the Smartsale platform, including utility functions, blockchain interaction helpers, and common components used across the project. +The Tokens package contains definitions, interfaces, and utilities related to the various tokens used within the SmartSale ecosystem. This includes ERC20 token implementations, token standards, and related blockchain interactions. -#### TSConfig (`/packages/tsconfig`) +For more details on specific packages, refer to their respective README files: -Houses TypeScript configuration files used to compile TypeScript projects within the monorepo. It ensures consistency in TypeScript compilation settings. +- [Alchemy](./packages/alchemy/README.md) +- [Jobs](./packages/jobs/README.md) +- [Supabase](./packages/supabase/README.md) +- [Tokens](./packages/tokens/README.md) +You can also check their package.json configurations for additional information. ### Hardhat (`/hardhat`) diff --git a/apps/faucet/.gitignore b/apps/faucet/.gitignore index a547bf36d..ea93fc9b6 100644 --- a/apps/faucet/.gitignore +++ b/apps/faucet/.gitignore @@ -8,7 +8,8 @@ pnpm-debug.log* lerna-debug.log* node_modules -dist +dist/** +!dist/.keep dist-ssr *.local diff --git a/apps/faucet/package.json b/apps/faucet/package.json index 2775c0ccd..7c336afe9 100644 --- a/apps/faucet/package.json +++ b/apps/faucet/package.json @@ -24,7 +24,7 @@ "react": "18.x", "react-dom": "18.x", "app-env": "workspace:*", - "app-contracts": "workspace:*", + "@repo/contracts": "workspace:*", "tailwind-merge": "^2.4.0", "tailwindcss": "^3.4.6", "tailwindcss-animate": "^1.0.7", diff --git a/apps/faucet/src/components/add-token-to-metamask.tsx b/apps/faucet/src/components/add-token-to-metamask.tsx index e3441f1b9..24f31a26e 100644 --- a/apps/faucet/src/components/add-token-to-metamask.tsx +++ b/apps/faucet/src/components/add-token-to-metamask.tsx @@ -1,4 +1,4 @@ -import type { EVMTokenContractData } from 'app-contracts' +import type { EVMTokenContractData } from '@repo/contracts' import { useSwitchChain } from 'wagmi' import { Button } from './ui/button' diff --git a/apps/faucet/src/components/faucet-form.tsx b/apps/faucet/src/components/faucet-form.tsx index 7e955b4ed..147a26d49 100644 --- a/apps/faucet/src/components/faucet-form.tsx +++ b/apps/faucet/src/components/faucet-form.tsx @@ -7,7 +7,7 @@ import { TestnetMBOTSPL, TestnetUSDCred, TestnetUSDT, -} from 'app-contracts' +} from '@repo/contracts' import { useEffect, useState } from 'react' import { parseUnits } from 'viem' import { useAccount, useSwitchChain, useWriteContract } from 'wagmi' diff --git a/apps/faucet/src/components/token-select.tsx b/apps/faucet/src/components/token-select.tsx index 00d57ea49..ce9ee964c 100644 --- a/apps/faucet/src/components/token-select.tsx +++ b/apps/faucet/src/components/token-select.tsx @@ -8,7 +8,7 @@ import { SelectValue, } from '@/components/ui/select' import type { SelectProps } from '@radix-ui/react-select' -import type { TokenContractData } from 'app-contracts' +import type { TokenContractData } from '@repo/contracts' export function TokenSelect({ options, ...props }: TokenSelectParams) { return ( diff --git a/apps/faucet/src/hooks/use-usdt-balance.ts b/apps/faucet/src/hooks/use-usdt-balance.ts index acac7e1b5..760776b5e 100644 --- a/apps/faucet/src/hooks/use-usdt-balance.ts +++ b/apps/faucet/src/hooks/use-usdt-balance.ts @@ -1,4 +1,4 @@ -import { TestnetUSDCred } from 'app-contracts' +import { TestnetUSDCred } from '@repo/contracts' import { formatUnits } from 'viem' import { useAccount, useReadContract } from 'wagmi' diff --git a/apps/indexer/.env-sample b/apps/indexer/.env-sample index 0cd94f8ad..62b31dfe3 100644 --- a/apps/indexer/.env-sample +++ b/apps/indexer/.env-sample @@ -4,4 +4,6 @@ SEPOLIA_RPC=https://eth-sepolia.g.alchemy.com/v2/xxx ISSUER_KEY=xxx ISSUER_ADDRESS=0x DFUSE_API_KEY=server_xxx -ALCHEMY_ACTIVITY_SIGNING_KEY=xxx \ No newline at end of file +ALCHEMY_ACTIVITY_SIGNING_KEY=xxx +TRIGGER_SECRET_KEY=1234567890 +PRESALE_ADDRESS=0x diff --git a/apps/indexer/Dockerfile b/apps/indexer/Dockerfile index bfa217ddb..47987fd71 100644 --- a/apps/indexer/Dockerfile +++ b/apps/indexer/Dockerfile @@ -6,24 +6,25 @@ WORKDIR /app # Copy the package.json files for the workspaces COPY package.json . -COPY apps/supabase/package.json ./apps/supabase/package.json COPY apps/indexer/package.json ./apps/indexer/package.json -COPY apps/trigger/package.json ./apps/trigger/package.json +COPY packages/jobs/package.json ./packages/jobs/package.json +COPY packages/supabase/package.json ./packages/supabase/package.json +COPY packages/alchemy/package.json ./packages/alchemy/package.json COPY packages/app-env/package.json ./packages/app-env/package.json -COPY packages/app-contracts/package.json ./packages/app-contracts/package.json -COPY packages/app-lib/package.json ./packages/app-lib/package.json -COPY packages/config-typescript/package.json ./packages/config-typescript/package.json - +COPY packages/contracts/package.json ./packages/contracts/package.json +COPY packages/utils/package.json ./packages/utils/package.json +COPY packages/tsconfig/package.json ./packages/tsconfig/package.json +COPY packages/tokens/package.json ./packages/tokens/package.json # Copy the lock file COPY bun.lockb . # Install dependencies for the entire monorepo RUN mkdir hardhat -RUN npm install -g bun ts-node tsconfig-paths +RUN npm install -g bun +RUN bun install -g ts-node tsconfig-paths ts-node-dev RUN bun install # Copy the specific workspaces -COPY apps/supabase ./apps/supabase COPY apps/indexer ./apps/indexer COPY apps/trigger ./apps/trigger COPY packages/ ./packages/ diff --git a/apps/indexer/README.md b/apps/indexer/README.md index 52353ed14..11f536e22 100644 --- a/apps/indexer/README.md +++ b/apps/indexer/README.md @@ -1,5 +1,20 @@ # Bitlauncher Indexer +The Bitlauncher Indexer is a critical component of our blockchain data processing ecosystem. It's an advanced, high-performance system designed to efficiently capture, process, and store blockchain events in real-time. By continuously monitoring the blockchain, the indexer ensures that the Bitlauncher platform always has access to the most up-to-date and accurate on-chain data. + +Key responsibilities of the Bitlauncher Indexer include: + +- Real-time event monitoring: Continuously listens for new blocks and transactions on the blockchain. +- Smart contract interaction tracking: Indexes and processes events related to specific smart contracts. +- Token transfer tracking: Monitors and records all token transfer activities. +- Data normalization: Transforms raw blockchain data into a structured format for easy querying and analysis. +- Historical data management: Maintains a comprehensive historical record of blockchain activities. +- API integration: Provides a robust API for other components of the Bitlauncher platform to access indexed data. +- Performance optimization: Ensures high throughput and low latency in data processing and retrieval. +- Scalability: Designed to handle increasing blockchain activity and data volume. + +## Getting Started§ + ```bash # Copy environment variables. Put your dfuse credentials on it cp .env-sample .env diff --git a/apps/indexer/package.json b/apps/indexer/package.json index 9c35bb56a..21b6828a5 100644 --- a/apps/indexer/package.json +++ b/apps/indexer/package.json @@ -15,6 +15,9 @@ "dependencies": { "@dfuse/client": "^0.3.21", "@repo/supabase": "workspace:*", + "@repo/jobs": "workspace:*", + "@repo/alchemy": "workspace:*", + "@repo/tokens": "workspace:*", "@repo/trigger": "workspace:*", "@sentry/integrations": "^7.114.0", "@sentry/node": "^8.19.0", @@ -22,9 +25,9 @@ "@supabase/supabase-js": "^2.44.4", "@trigger.dev/sdk": "^2.3.19", "@types/pino-http": "^5.8.4", - "app-contracts": "workspace:*", + "@repo/contracts": "workspace:*", "app-env": "workspace:*", - "app-lib": "workspace:*", + "@repo/utils": "workspace:*", "axios": "^1.7.2", "bn.js": "^5.2.1", "express": "^4.19.2", @@ -35,7 +38,8 @@ "pino-http": "^10.2.0", "resend": "^3.5.0", "viem": "latest", - "ws": "^8.18.0" + "ws": "^8.18.0", + "zod": "^3.23.8" }, "devDependencies": { "@types/express": "^4.17.21", @@ -46,7 +50,7 @@ "rimraf": "^6.0.1", "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", - "@repo/typescript-config": "workspace:*", + "@repo/tsconfig": "workspace:*", "tsconfig-paths": "^4.2.0", "typescript": "^5.5.4" } diff --git a/apps/indexer/src/api/alchemy.ts b/apps/indexer/src/api/alchemy.ts new file mode 100644 index 000000000..51de3c1ea --- /dev/null +++ b/apps/indexer/src/api/alchemy.ts @@ -0,0 +1,150 @@ +import crypto from 'crypto' + +import type { + AlchemyActivityEvent, + AlchemyNetwork, + AlchemyWebhookEvent, +} from '@repo/alchemy' + +import { addressActivityTask } from '@repo/jobs' +import { evmTokens } from '@repo/tokens' +import { prodChains } from 'app-env' +import type { Request, Response } from 'express' +import { getAddress } from 'viem' +import { appConfig } from '~/config' +import { logger } from '~/lib/logger' +import { + getPresaleData, + isAddressRegisteredForPresale, +} from '~/lib/supabase-client' +// import {isAddressRegisteredForPresale} from '~/src/lib/supabase-client'; + +// Mapping of chain IDs to Alchemy SDK Network types +const chainIdToNetwork: Record = { + 1: 'ETH_MAINNET', + 137: 'MATIC_MAINNET', + 42161: 'ARB_MAINNET', + 10: 'OPT_MAINNET', + 8453: 'BASE_MAINNET', + 43114: 'MATIC_MAINNET', + 56: 'BNB_MAINNET', +} + +// Create an array of supported networks based on production chains +const networks: AlchemyNetwork[] = prodChains.map((chain) => { + const network = chainIdToNetwork[chain.id] + if (!network) throw new Error(`Unsupported chain ID: ${chain.id}`) + return network +}) +logger.info(`Supported networks: ${networks.join(', ')}`) + +/** + * Handles incoming Alchemy webhook requests. + * Validates the signature, logs the request, and triggers the address activity task. + * @param req - The incoming request object + * @param res - The response object + */ +export async function alchemyWebhook(req: Request, res: Response) { + // Parse the incoming webhook event + const evt = req.body as AlchemyWebhookEvent + logger.info(`Alchemy webhook received: ${JSON.stringify(evt)}`) + + // TODO: restore alchemy signature validation + // Validate the Alchemy signature + if (!validateAlchemySignature(req)) + return res.status(401).send('Unauthorized') + + // Extract network and activity from the event + const { network, activity } = evt.event as AlchemyActivityEvent + + // Validate event type and network + const isAddressActivity = evt.type === 'ADDRESS_ACTIVITY' + const isValidNetwork = networks.includes(network) + + if (!isAddressActivity || !isValidNetwork) { + const errorMsg = !isAddressActivity + ? `event type: ${evt.type}` + : `network: ${network}` + logger.error(`Invalid: ${errorMsg}`) + return res.status(401).send('Unauthorized') + } + + // Validate each transaction in the activity + for (const txn of activity) { + // Check if the asset is valid (USDC or USDT) + const isValidAsset = txn.asset === 'USDC' || txn.asset === 'USDT' + // Ensure the transaction is not sent to the presale address + const isValidToAddress = txn.toAddress !== appConfig.presaleAddress + // Get the token address from the transaction + const txnTokenAddress = + txn.rawContract?.address && getAddress(txn.rawContract.address) + + // Check if the token is supported + const isSupportedToken = + txnTokenAddress && + evmTokens.some((token) => txnTokenAddress === getAddress(token.address)) + + // Check if the sender is registered for the presale + const isRegisteredForPresale = await isAddressRegisteredForPresale( + txn.fromAddress, + 1, + ) + + // Get presale data and validate amount and timing + const presaleData = await getPresaleData(1) + const isValidAmount = txn.value <= presaleData.max_allocation + const currentTimestamp = Math.floor(Date.now() / 1000) // Current time in seconds + const isWithinPresalePeriod = + currentTimestamp >= Number(presaleData.start_timestamptz) && + currentTimestamp <= Number(presaleData.end_timestamptz) + + // Collect all validation errors + const validationErrors = [ + !isValidAsset && `asset: ${txn.asset}`, + !isValidToAddress && `to address: ${txn.toAddress}`, + !isSupportedToken && `token: ${txnTokenAddress}`, + !txn.log && 'missing transaction log', + !isRegisteredForPresale && 'address is not registered for presale', + !isValidAmount && + `amount exceeds max allocation: ${txn.value} > ${presaleData.max_allocation}`, + !isWithinPresalePeriod && + `transaction outside presale period: current time ${currentTimestamp}, presale start ${presaleData.start_timestamptz}, presale end ${presaleData.end_timestamptz}`, + ].filter(Boolean) + + // If there are any validation errors, log them and return unauthorized + if (validationErrors.length) { + logger.error(`Invalid transaction: ${validationErrors.join(', ')}`) + return res.status(401).send('Unauthorized') + } + + // Trigger the address activity task + const handle = await addressActivityTask.trigger(req.body) + logger.info(`Triggered address activity task: ${JSON.stringify(handle)}`) + } + + logger.info(`Webhook ${evt.id} processed`) + + // Send a success response + res.status(200).send(`Webhook ${evt.id} processed`) +} + +/** + * Validates the Alchemy webhook signature. + * @param req - The incoming request object + * @returns boolean - True if the signature is valid, false otherwise + */ +function validateAlchemySignature(req: Request): boolean { + // Extract the Alchemy signature from the request headers + const alchemySignature = req.headers['x-alchemy-signature'] as string + // Stringify the request body + const payload = JSON.stringify(req.body) + // Create an HMAC using the Alchemy signing key + const hmac = crypto.createHmac( + 'sha256', + appConfig.evm.alchemy.activitySigningKey, + ) + // Update the HMAC with the payload + hmac.update(payload) + // Compare the calculated signature with the provided signature + return alchemySignature === hmac.digest('hex') +} diff --git a/apps/indexer/src/routes/healthcheck.ts b/apps/indexer/src/api/healthcheck.ts similarity index 100% rename from apps/indexer/src/routes/healthcheck.ts rename to apps/indexer/src/api/healthcheck.ts diff --git a/apps/indexer/src/api/index.ts b/apps/indexer/src/api/index.ts new file mode 100644 index 000000000..f8db0cdd8 --- /dev/null +++ b/apps/indexer/src/api/index.ts @@ -0,0 +1,98 @@ +import express, { + type NextFunction, + type Response, + type Request, +} from 'express' +import rateLimit from 'express-rate-limit' +import helmet from 'helmet' +import pinoHttp from 'pino-http' +import { logger } from '~/lib/logger' +import { setupSentryErrorHandler } from '~/lib/sentry' +import { alchemyWebhook } from './alchemy' +import { healthcheck } from './healthcheck' + +export function startExpress() { + const app = express() + const port = 8080 + + // Trust proxy + app.set('trust proxy', 1) + + // Security Middlewares + app.use(helmet()) + app.use(express.json({ limit: '1mb' })) + + // Rate limiting + app.use( + rateLimit({ + windowMs: 60 * 1000, // 1 minute + max: 100, // limit each IP to 100 requests per windowMs + standardHeaders: true, + legacyHeaders: false, + }), + ) + + // Sentry error handler + setupSentryErrorHandler(app) + + // Logging middleware + // app.use(pinoHttp({ + // logger, + // serializers: { + // req: (req) => JSON.stringify({ + // method: req.method, + // url: req.url, + // headers: req.headers, + // }), + // res: (res) => JSON.stringify({ + // statusCode: res.statusCode, + // }), + // err: (err) => JSON.stringify({ + // type: err.constructor.name, + // message: err.message, + // stack: err.stack, + // }), + // }, + // })) + + // Routes + app.get('/', healthcheck) + app.post('/alchemy', alchemyWebhook) + + // Error handling middleware + app.use((err: Error, req: Request, res: Response, next: NextFunction) => { + logger.error( + { err, req: { method: req.method, url: req.url } }, + 'Unhandled error', + ) + res.status(500).json({ error: 'Internal Server Error' }) + }) + + // Start the server + const server = app.listen(port, () => { + logger.info(`Server running at http://localhost:${port}`) + }) + + // Handle unhandled promise rejections + process.on('unhandledRejection', (reason: any, promise: Promise) => { + logger.error({ reason, promise }, 'Unhandled Rejection') + // Optionally, you can choose to exit the process + // process.exit(1) + }) + + // Handle uncaught exceptions + process.on('uncaughtException', (error: Error) => { + logger.error(JSON.stringify(error), 'Uncaught Exception') + // It's generally recommended to exit the process on uncaught exceptions + process.exit(1) + }) + + // Graceful shutdown + process.on('SIGTERM', () => { + logger.info('SIGTERM signal received: closing HTTP server') + server.close(() => { + logger.info('HTTP server closed') + process.exit(0) + }) + }) +} diff --git a/apps/indexer/src/config.ts b/apps/indexer/src/config.ts index b553a7af1..aee2589c2 100644 --- a/apps/indexer/src/config.ts +++ b/apps/indexer/src/config.ts @@ -1,22 +1,63 @@ import { smartsaleEnv } from 'app-env' import type { Address } from 'viem' +import { isAddress } from 'viem' import { privateKeyToAccount } from 'viem/accounts' +import { z } from 'zod' +import { logger } from './lib/logger' + +const envSchema = z.object({ + SENTRY_DSN: z.string().min(1), + DFUSE_API_KEY: z.string().min(1), + TRIGGER_SECRET_KEY: z.string().min(1), + SEPOLIA_RPC: z.string().url(), + ISSUER_KEY: z + .string() + .min(1) + .length(64) + .regex(/^[a-f0-9]+$/i, 'Invalid issuer key format'), + ISSUER_ADDRESS: z + .string() + .refine( + (value): value is Address => isAddress(value), + 'Invalid issuer address', + ), + PRESALE_ADDRESS: z + .string() + .refine( + (value): value is Address => isAddress(value), + 'Invalid presale address', + ), + + ALCHEMY_ACTIVITY_SIGNING_KEY: z.string().min(1), +}) + +const parsedEnv = envSchema.safeParse(process.env) +if (!parsedEnv.success) { + logger.error( + `Environment validation failed: ${JSON.stringify(parsedEnv.error.format())}`, + ) + process.exit(1) +} export const appConfig = { + presaleAddress: parsedEnv.data.PRESALE_ADDRESS, sentry: { - dsn: process.env.SENTRY_DSN || '', + dsn: parsedEnv.data.SENTRY_DSN, }, eos: { - dfuseKey: process.env.DFUSE_API_KEY || '', + dfuseKey: parsedEnv.data.DFUSE_API_KEY, + }, + trigger: { + secretKey: parsedEnv.data.TRIGGER_SECRET_KEY, }, evm: { eosApi: 'https://api.testnet.evm.eosnetwork.com', - sepoliaApi: process.env.SEPOLIA_RPC || '', - issuerKey: process.env.ISSUER_KEY || '', - issuerAddress: (process.env.ISSUER_ADDRESS || '') as Address, - issuerAccount: privateKeyToAccount(`0x${process.env.ISSUER_KEY}`), + sepoliaApi: parsedEnv.data.SEPOLIA_RPC, + issuerKey: parsedEnv.data.ISSUER_KEY, + issuerAddress: parsedEnv.data.ISSUER_ADDRESS as Address, + issuerAccount: privateKeyToAccount(`0x${parsedEnv.data.ISSUER_KEY}`), alchemy: { - activitySigningKey: process.env.ALCHEMY_ACTIVITY_SIGNING_KEY || '', + activitySigningKey: parsedEnv.data.ALCHEMY_ACTIVITY_SIGNING_KEY, }, }, ...smartsaleEnv.test, diff --git a/apps/indexer/src/index.ts b/apps/indexer/src/index.ts index d4901d186..956bc5a98 100644 --- a/apps/indexer/src/index.ts +++ b/apps/indexer/src/index.ts @@ -1,9 +1,9 @@ -import { getErrorMessage } from 'app-lib' +import { getErrorMessage } from '@repo/utils' +import { startExpress } from './api' import { logger } from './lib/logger' -import { startExpress } from './routes/index' async function main() { - logger.info(`Launchpad indexer starting up ...`) + logger.info('Launchpad indexer starting up ...') try { startExpress() // startPresaleService() diff --git a/apps/indexer/src/lib/supabase-client.ts b/apps/indexer/src/lib/supabase-client.ts index 850c8ffc3..bf588c328 100644 --- a/apps/indexer/src/lib/supabase-client.ts +++ b/apps/indexer/src/lib/supabase-client.ts @@ -46,16 +46,54 @@ export async function upsertOrder(data: TablesInsert<'order'>) { return data } -export async function upsertTransfers(data: TablesInsert<'transfer'>) { - console.log('upsert transfers', data) - const { data: result, error } = await supabase - .from('transfer') - .upsert(data, { onConflict: 'trx_hash' }) - .select() +// export async function upsertTransfers(data: TablesInsert<'transfer'>) { +// console.log('upsert transfers', data) +// const { data: result, error } = await supabase +// .from('transfer') +// .upsert(data, { onConflict: 'trx_hash' }) +// .select() - if (error) console.error('Error:', error) +// if (error) console.error('Error:', error) - console.log('Result:', result) +// console.log('Result:', result) + +// return data +// } + +export async function isAddressRegisteredForPresale( + address: string, + projectId: number, +): Promise { + const { data, error } = await supabase + .from('whitelist') + .select('*') + .eq('address', address) + .eq('id', projectId) + .single() + + if (error) { + if (error.code === 'PGRST116') { + // No matching row found, address is not registered + return false + } + console.error('Error checking presale registration:', error) + throw error + } + + return !!data +} + +export async function getPresaleData(projectId: number) { + const { data, error } = await supabase + .from('presale') + .select('*') + .eq('id', projectId) + .single() + + if (error) { + console.error('Error checking presale data:', error) + throw error + } return data } diff --git a/apps/indexer/src/modules/auction/auction-indexer.ts b/apps/indexer/src/modules/auction/auction-indexer.ts index b9384c63e..2c4137acf 100644 --- a/apps/indexer/src/modules/auction/auction-indexer.ts +++ b/apps/indexer/src/modules/auction/auction-indexer.ts @@ -1,7 +1,7 @@ -import { TestnetEasyAuction } from 'app-contracts' +import { TestnetEasyAuction } from '@repo/contracts' import { eosEvmTestnet } from 'app-env' import BN from 'bn.js' -import { http, type Log, createPublicClient, stringify } from 'viem' +import { createPublicClient, http, stringify, type Log } from 'viem' import { upsertAuctionDetail, upsertOrder } from '~/lib/supabase-client' import { bigintToPostgresTimestamp, diff --git a/apps/indexer/src/modules/auction/easyauction.ts b/apps/indexer/src/modules/auction/easyauction.ts index 970900370..e1cdf14ef 100644 --- a/apps/indexer/src/modules/auction/easyauction.ts +++ b/apps/indexer/src/modules/auction/easyauction.ts @@ -1,6 +1,6 @@ -import { TestnetEasyAuction } from 'app-contracts' +import { TestnetEasyAuction } from '@repo/contracts' import { eosEvmTestnet } from 'app-env' -import { http, createPublicClient, getContract } from 'viem' +import { createPublicClient, getContract, http } from 'viem' const client = createPublicClient({ chain: eosEvmTestnet, diff --git a/apps/indexer/src/modules/presale/evm-contributions.ts b/apps/indexer/src/modules/presale/evm-contributions.ts index 9783f7edb..7f4b59701 100644 --- a/apps/indexer/src/modules/presale/evm-contributions.ts +++ b/apps/indexer/src/modules/presale/evm-contributions.ts @@ -1,14 +1,13 @@ -import { type EVMTokenContractData, appContracts } from 'app-contracts' +import { type EVMTokenContractData, appContracts } from '@repo/contracts' import { - http, type Address, type Log, type PublicClient, createPublicClient, - parseAbiItem, - stringify, + http, + parseAbiItem } from 'viem' -import { upsertTransfers } from '~/lib/supabase-client' +// import { upsertTransfers } from '~/lib/supabase-client' import { runPromisesInSeries } from '~/lib/utils' import type { TransferEvent } from '~/modules/auction/auction.type' import { issuePresaleTokens } from './presale-issuer' @@ -104,16 +103,16 @@ async function handleTransfer(log: TransferEvent, token: EVMTokenContractData) { log.args.value, )) as Address - upsertTransfers({ - trx_hash: log.transactionHash!, - from: log.args.from as Address, - to: log.args.to as Address, - amount: Number(log.args.value), - token: log.address, - chain_id: token.chainId, - type: 'presale', - bl_presale_trx, - }) + // upsertTransfers({ + // trx_hash: log.transactionHash!, + // from: log.args.from as Address, + // to: log.args.to as Address, + // amount: Number(log.args.value), + // token: log.address, + // chain_id: token.chainId, + // type: 'presale', + // bl_presale_trx, + // }) // console.log('result', result) // if (result.usdcred_trx || data.from === '0x0000000000000000000000000000000000000000') return diff --git a/apps/indexer/src/modules/presale/presale-issuer.ts b/apps/indexer/src/modules/presale/presale-issuer.ts index 9bbe3e4e8..7e001d6ad 100644 --- a/apps/indexer/src/modules/presale/presale-issuer.ts +++ b/apps/indexer/src/modules/presale/presale-issuer.ts @@ -1,4 +1,4 @@ -import { TestnetBLPL } from 'app-contracts' +import { TestnetBLPL } from '@repo/contracts' import { eosEvmTestnet } from 'app-env' import { createWalletClient } from 'viem' import { http, type Address } from 'viem' diff --git a/apps/indexer/src/modules/swaps/cred-issuer.ts b/apps/indexer/src/modules/swaps/cred-issuer.ts index d67c5a958..fb9b5c3ce 100644 --- a/apps/indexer/src/modules/swaps/cred-issuer.ts +++ b/apps/indexer/src/modules/swaps/cred-issuer.ts @@ -1,4 +1,4 @@ -import { TestnetUSDCred } from 'app-contracts' +import { TestnetUSDCred } from '@repo/contracts' import { eosEvmTestnet } from 'app-env' import { createWalletClient } from 'viem' import { http, type Address } from 'viem' diff --git a/apps/indexer/src/modules/swaps/evm-transfers.ts b/apps/indexer/src/modules/swaps/evm-transfers.ts index 9494a0717..91dce8991 100644 --- a/apps/indexer/src/modules/swaps/evm-transfers.ts +++ b/apps/indexer/src/modules/swaps/evm-transfers.ts @@ -1,18 +1,16 @@ import { type EVMTokenContractData, - SepoliaUSDT, - TestnetUSDT, - appContracts, -} from 'app-contracts' + appContracts +} from '@repo/contracts' import { runPromisesInSeries } from '~/lib/utils' import { appChains } from 'app-env' import { - http, type Address, type Log, type PublicClient, createPublicClient, + http, parseAbiItem, stringify, } from 'viem' diff --git a/apps/indexer/tsconfig.json b/apps/indexer/tsconfig.json index d14168838..6432ecae3 100644 --- a/apps/indexer/tsconfig.json +++ b/apps/indexer/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../packages/config-typescript/node16.json", + "extends": "../../packages/tsconfig/node16.json", "include": ["./src/**/*", "src/lib/utils.ts"], "exclude": ["node_modules"], "compilerOptions": { diff --git a/apps/trigger/package.json b/apps/trigger/package.json index 8661d531a..8ed853c01 100644 --- a/apps/trigger/package.json +++ b/apps/trigger/package.json @@ -9,9 +9,9 @@ "deploy:prod": "bunx trigger.dev@beta deploy --env prod" }, "dependencies": { - "app-contracts": "workspace:*", + "@repo/alchemy": "workspace:*", "app-env": "workspace:*", - "app-lib": "workspace:*", + "@repo/utils": "workspace:*", "@trigger.dev/sdk": "3.0.0-beta.55", "viem": "latest" } diff --git a/apps/trigger/tsconfig.json b/apps/trigger/tsconfig.json index 97c2d81b0..c24fa5e65 100644 --- a/apps/trigger/tsconfig.json +++ b/apps/trigger/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../packages/config-typescript/node16.json", + "extends": "@repo/tsconfig/node16.json", "include": ["./src/**/*", "src/lib/utils.ts", "trigger.config.ts"], "exclude": ["node_modules"], "compilerOptions": { diff --git a/apps/webapp/actions.ts b/apps/webapp/actions.ts index 62c9f3ea9..5ad424bb7 100644 --- a/apps/webapp/actions.ts +++ b/apps/webapp/actions.ts @@ -3,7 +3,7 @@ import { handleAxiosError } from '@/lib/utils' import { createSupabaseServerClient } from '@/services/supabase' import { presaleInsertSchema } from '@repo/supabase' -import { fromEntries } from 'app-lib' +import { fromEntries } from '@repo/utils' import axios from 'axios' import { cookies } from 'next/headers' import { Resend } from 'resend' diff --git a/apps/webapp/app/(routes)/[lang]/[project]/auction/page.tsx b/apps/webapp/app/(routes)/[lang]/[project]/auction/page.tsx index 773e51b7c..4e4f7fd42 100644 --- a/apps/webapp/app/(routes)/[lang]/[project]/auction/page.tsx +++ b/apps/webapp/app/(routes)/[lang]/[project]/auction/page.tsx @@ -17,6 +17,8 @@ import { getProjectBySlug, getProjects, } from '@/lib/projects' +import { createSupabaseServerClient } from '@/services/supabase' +import { getPresaleData } from '@/services/supabase/service' import type { ProjectPageParams, ProjectPageProps } from '@/types/routing.type' import { redirect } from 'next/navigation' @@ -25,6 +27,8 @@ export default async function AuctionPage({ params }: ProjectPageProps) { const p = await getProjectBySlug(params.project, dict) if (!p || (!p.auctionId && !p.registrationOpen)) redirect('/') const project = p as ProjectWithAuction + const supabase = await createSupabaseServerClient() + const presaleData = await getPresaleData({ projectId: project.id, supabase }) return (
@@ -32,9 +36,9 @@ export default async function AuctionPage({ params }: ProjectPageProps) {
- + - + diff --git a/apps/webapp/app/(routes)/[lang]/[project]/page.tsx b/apps/webapp/app/(routes)/[lang]/[project]/page.tsx index 76d269e24..feb8992e4 100644 --- a/apps/webapp/app/(routes)/[lang]/[project]/page.tsx +++ b/apps/webapp/app/(routes)/[lang]/[project]/page.tsx @@ -17,10 +17,13 @@ import { redirect } from 'next/navigation' export default async function ProjectPage({ params }: ProjectPageProps) { const dict = await getDictionary(params.lang) const project = await getProjectBySlug(params.project, dict) - if (!project) redirect('/') + if (!project || !project.content) redirect('/') - const projectContentObjectKeys = Object.keys(project.content!) - const projectContent = project.content! + const projectContentObjectKeys = Object.keys(project.content) + const projectContent = project.content + + // is presale upcoming + // const isPresaleUpcoming = new Date(project.presaleData.start_timestamptz) > new Date() return ( <> @@ -28,7 +31,7 @@ export default async function ProjectPage({ params }: ProjectPageProps) {
- +
@@ -56,7 +59,7 @@ export default async function ProjectPage({ params }: ProjectPageProps) { !isLastItem && 'backdrop-x my-10 overflow-hidden rounded-3xl bg-primary/70 pb-10', index % 2 !== 0 - ? `backdrop-x rounded-3xl bg-primary/70 pb-10` + ? 'backdrop-x rounded-3xl bg-primary/70 pb-10' : '', )} > diff --git a/apps/webapp/app/(routes)/[lang]/[project]/presale/page.tsx b/apps/webapp/app/(routes)/[lang]/[project]/presale/page.tsx index 6a7c92866..954d95250 100644 --- a/apps/webapp/app/(routes)/[lang]/[project]/presale/page.tsx +++ b/apps/webapp/app/(routes)/[lang]/[project]/presale/page.tsx @@ -6,6 +6,8 @@ import { Countdown } from '@/components/shared/countdown' import { Card, CardContent } from '@/components/ui/card' import { getDictionary } from '@/dictionaries' import { type ProjectWithAuction, getProjectBySlug } from '@/lib/projects' +import { createSupabaseServerClient } from '@/services/supabase/server' +import { getPresaleData, getProjectData } from '@/services/supabase/service' import type { ProjectPageProps } from '@/types/routing.type' import { redirect } from 'next/navigation' @@ -16,15 +18,22 @@ export default async function ProjectPage({ params }: ProjectPageProps) { dict, )) as ProjectWithAuction if (!project) redirect('/') + const supabase = await createSupabaseServerClient() + const presaleData = await getPresaleData({ projectId: project.id, supabase }) + const projectData = await getProjectData({ projectId: project.id, supabase }) + if (!project) redirect('/') return (
- + - + diff --git a/apps/webapp/app/(routes)/[lang]/about/about-bitlauncher/page.tsx b/apps/webapp/app/(routes)/[lang]/about/about-bitlauncher/page.tsx index 846781a3a..3c66161ae 100644 --- a/apps/webapp/app/(routes)/[lang]/about/about-bitlauncher/page.tsx +++ b/apps/webapp/app/(routes)/[lang]/about/about-bitlauncher/page.tsx @@ -9,7 +9,7 @@ import type { Metadata } from 'next' export default async function AboutBitlauncher({ params }: CommonPageProps) { const dict = await getDictionary(params.lang) return ( -
+
+ Bitlauncher is a pioneering launchpad dedicated to + transforming the landscape of artificial intelligence (AI) and + cryptocurrency. We are on a mission to empower the next wave of AI + innovation by providing open-source AI projects with equitable fundraising + opportunities and decentralized organization through the use of blockchain + technology. + + ), + paragraph2: ( + <> + At Bitlauncher, we{' '} + + combine the transformative powers of AI and cryptocurrency + {' '} + to address the unique challenges faced by AI startups. By integrating + tokenization and decentralized autonomous organizations (DAOs), we create + a seamless synergy that enables these startups to overcome funding + barriers, accelerate their growth, and harness global resources. + + ), + paragraph3: ( + <> + Our platform is built on a foundation of{' '} + transparency, inclusivity, and community-driven progress.{' '} + We foster a collaborative environment where developers, contributors, and + AI enthusiasts can come together to share resources, exchange ideas, and + shape the future of technology. + + ), image: { - alt: 'dBoard', + alt: 'about-bg', src: '/images/about-bg.svg', width: 610, height: 468, diff --git a/apps/webapp/app/(routes)/[lang]/blog/[category]/page.tsx b/apps/webapp/app/(routes)/[lang]/blog/[category]/page.tsx index 8776864d0..dc29c8e0d 100644 --- a/apps/webapp/app/(routes)/[lang]/blog/[category]/page.tsx +++ b/apps/webapp/app/(routes)/[lang]/blog/[category]/page.tsx @@ -8,7 +8,6 @@ import { getPageSeoText, } from '@/services/datocms' import type { Metadata } from 'next' -import Image from 'next/image' import { notFound } from 'next/navigation' export default async function Page(props: CategoryPageProps) { @@ -23,17 +22,17 @@ export default async function Page(props: CategoryPageProps) { if (!pageSeo) notFound() return ( - <> +
- -
+
-
- +
+ ) } diff --git a/apps/webapp/app/(routes)/[lang]/learn/security/page.tsx b/apps/webapp/app/(routes)/[lang]/learn/security/page.tsx index cb0cf6067..5355e5685 100644 --- a/apps/webapp/app/(routes)/[lang]/learn/security/page.tsx +++ b/apps/webapp/app/(routes)/[lang]/learn/security/page.tsx @@ -1,10 +1,10 @@ -import { WhyChooseUs } from '@/components/routes/home/why-choose-us' +import { LearnSection } from '@/components/layout/section/learn-section' +import StepsSection from '@/components/layout/section/steps-section' import { BgHeader } from '@/components/shared/bg-header' import { PageContent } from '@/components/shared/content' import { getDictionary } from '@/dictionaries' import type { CommonPageProps } from '@/types/routing.type' import type { Metadata } from 'next' -import React from 'react' export default async function SecurityTips({ params }: CommonPageProps) { const dict = await getDictionary(params.lang) @@ -13,13 +13,19 @@ export default async function SecurityTips({ params }: CommonPageProps) { return ( <> - -
- +
+ +
+ +
+
+
+ +
) diff --git a/apps/webapp/app/(routes)/[lang]/page.tsx b/apps/webapp/app/(routes)/[lang]/page.tsx index 6730324bc..20821ff56 100644 --- a/apps/webapp/app/(routes)/[lang]/page.tsx +++ b/apps/webapp/app/(routes)/[lang]/page.tsx @@ -3,6 +3,10 @@ import { Categories } from '@/components/_wip/categories' import { FeatureOne } from '@/components/_wip/feature-one' import { FeatureThree } from '@/components/_wip/feature-three' import { FeatureTwo } from '@/components/_wip/feature-two' +import { RecentArticles } from '@/components/layout/section/article-section' +import { FAQ } from '@/components/layout/section/faq-section' +import { LearnSection } from '@/components/layout/section/learn-section' +import StepsSection from '@/components/layout/section/steps-section' import { NewHomeHero } from '@/components/routes/home/hero/index' import { getDictionary } from '@/dictionaries' import type { Lang } from '@/dictionaries/locales' @@ -33,17 +37,17 @@ export default async function IndexPage({ params: { lang } }: IndexPageProps) { const projects = getProjects(dict) return ( -
+
- {appConfig.features.sections ? ( - <> - - - - ) : null} + + + + + + {appConfig.features.explorations ? ( <> diff --git a/apps/webapp/app/(routes)/[lang]/whitepaper/page.tsx b/apps/webapp/app/(routes)/[lang]/whitepaper/page.tsx index 736a457ad..d8b1c3e91 100644 --- a/apps/webapp/app/(routes)/[lang]/whitepaper/page.tsx +++ b/apps/webapp/app/(routes)/[lang]/whitepaper/page.tsx @@ -10,15 +10,17 @@ export default async function BitlauncherWhitePaper({ const dict = await getDictionary(params.lang) return ( <> - +
+ -
- -
+
+ +
+
) } diff --git a/apps/webapp/app/actions/save-deposit.ts b/apps/webapp/app/actions/save-deposit.ts index 5598efd3b..e732cb7e2 100644 --- a/apps/webapp/app/actions/save-deposit.ts +++ b/apps/webapp/app/actions/save-deposit.ts @@ -4,17 +4,19 @@ import { createSupabaseServerClient } from '@/services/supabase' import { type Tables, type TablesInsert, - transferInsertSchema, + presaleDepositInsertSchema, } from '@repo/supabase' -export async function saveDeposit(transfer: TablesInsert<'transfer'>): Promise<{ +export async function saveDeposit( + transfer: TablesInsert<'presale_deposit'>, +): Promise<{ success: boolean message: string - data?: Tables<'transfer'> + data?: Tables<'presale_deposit'> error?: any }> { try { - const parseResult = transferInsertSchema.safeParse(transfer) + const parseResult = presaleDepositInsertSchema.safeParse(transfer) if (!parseResult.success) { return { success: false, @@ -24,8 +26,28 @@ export async function saveDeposit(transfer: TablesInsert<'transfer'>): Promise<{ } const supabase = await createSupabaseServerClient() + const transaction = await supabase + .from('transaction') + .upsert( + { + hash: parseResult.data.deposit_hash, + trx_type: 'presale_deposit', + ...parseResult.data, + }, + { onConflict: 'hash' }, + ) + .select() + + if (transaction.error) { + return { + success: false, + message: 'Database operation failed', + error: 'transaction.error', + } + } + const { data, error } = await supabase - .from('transfer') + .from('presale_deposit') .upsert(parseResult.data, { onConflict: 'trx_hash' }) .select() diff --git a/apps/webapp/app/globals.css b/apps/webapp/app/globals.css index 76383a63c..491bd1b2a 100644 --- a/apps/webapp/app/globals.css +++ b/apps/webapp/app/globals.css @@ -123,7 +123,7 @@ html { body, p { - @apply font-futura-pt-demi text-base font-normal; + @apply text-base font-normal font-futura-pt-demi; } h1, @@ -132,7 +132,7 @@ h3, h4, h5, h6 { - @apply font-bold tracking-tight; + @apply tracking-tight font-futura-pt-bold; } .open-sans { @@ -191,17 +191,17 @@ input[type="number"] { } .newsletter-wrapper { - @apply mx-auto box-content flex flex-col items-center px-0 sm:rounded-3xl sm:text-center; + @apply box-content flex flex-col items-center px-0 mx-auto sm:rounded-3xl sm:text-center; background: url("/images/newsletter-bg.webp") center center no-repeat; background-size: cover; } .narrow-container { - @apply container md:px-20; + @apply container md:px-10 xl:px-20; } .content-container { - @apply mx-auto flex w-full max-w-screen-md flex-col gap-10 px-5 py-10 md:py-24; + @apply flex flex-col w-full max-w-screen-md gap-10 px-5 py-10 mx-auto md:py-24; } /** Scrollbar Styling */ @@ -245,6 +245,17 @@ input[type="number"] { } } +iframe[title="reCAPTCHA"], +.grecaptcha-badge { + display: relative; + z-index: 0; + opacity: 0.333; + + &:hover { + opacity: 1; + } +} + /** Tooltip Styling */ *[data-title]::after { @@ -283,12 +294,31 @@ input[type="number"] { } /* TYPOGRAPHY */ + +.heroHeading { + @apply text-6xl font-bold leading-[76px]; +} + .heading { - @apply text-4xl font-bold md:text-5xl; + @apply text-4xl font-bold leading-normal md:text-4xl; +} + +.newsletterHeading { + @apply text-center text-[40.50px] font-extrabold leading-[45px] md:text-4xl sm:text-4xl; +} + +/* Heading in About - Security - Whitepaper */ +.sectionsHeading { + @apply text-[4.188rem] font-normal leading-normal md:text-[4.3504rem] lg:text-[5.438rem]; +} + +/* Subheading in About - Security - Whitepaper */ +.sectionsSubheading { + @apply text-start text-2xl font-medium leading-[42.35px] text-[#ff51ed] md:text-[2.188rem]; } .heading2 { - @apply text-2xl font-bold md:text-4xl; + @apply text-2xl font-bold leading-[42px] md:text-4xl; } .heading3 { @@ -308,10 +338,10 @@ input[type="number"] { } .mobile-nav { - height: calc(100vh - 64px) !important; + height: calc(100vh - 70px) !important; width: 100vw; overflow: hidden; - @apply fixed inset-x-0 top-16 flex flex-col items-center justify-evenly overflow-hidden bg-background pb-20; + @apply fixed inset-x-0 flex flex-col items-center pb-20 overflow-hidden top-16 justify-evenly bg-background; } /* INFOPAGES BG COVER*/ diff --git a/apps/webapp/components/_wip/early-access-landing.tsx b/apps/webapp/components/_wip/early-access-landing.tsx index e991dfb8b..b00fa9131 100644 --- a/apps/webapp/components/_wip/early-access-landing.tsx +++ b/apps/webapp/components/_wip/early-access-landing.tsx @@ -31,7 +31,7 @@ function Section({

{title}

-

+

{description}

@@ -54,7 +54,7 @@ function Section({
  • {project.name}

    -

    +

    {project.description}

    diff --git a/apps/webapp/components/_wip/feature-three.tsx b/apps/webapp/components/_wip/feature-three.tsx index 8397cf705..14ccdd481 100644 --- a/apps/webapp/components/_wip/feature-three.tsx +++ b/apps/webapp/components/_wip/feature-three.tsx @@ -11,7 +11,7 @@ export function FeatureThree() {

    Trusted by over 600 million users and 10,000 teams

    -

    +

    Our rigorous security and compliance standards are at the heart of all we do. We work tirelessly to protect you and your customers.

    @@ -75,7 +75,7 @@ export function FeatureThree() {

    99.99% uptime

    -

    +

    For Landwind, with zero maintenance downtime

  • @@ -91,7 +91,7 @@ export function FeatureThree() {

    600M+ Users

    -

    +

    Trusted by over 600 milion users around the world

    @@ -111,7 +111,7 @@ export function FeatureThree() {

    100+ countries

    -

    +

    Have used Landwind to create functional websites

    @@ -127,7 +127,7 @@ export function FeatureThree() {

    5+ Million

    -

    +

    Transactions per day

    diff --git a/apps/webapp/components/_wip/landing-page.tsx b/apps/webapp/components/_wip/landing-page.tsx index 61f69df9f..d903e0a9c 100644 --- a/apps/webapp/components/_wip/landing-page.tsx +++ b/apps/webapp/components/_wip/landing-page.tsx @@ -12,16 +12,14 @@ export function LandingPage({ content }: LandingPageProps) {

    {content.header}

    -

    +

    {content.description}

    {content.sections.map((section) => (

    {section.subHeader}

    -

    - {section.text} -

    +

    {section.text}

    ))}
    diff --git a/apps/webapp/components/_wip/landing-page2.tsx b/apps/webapp/components/_wip/landing-page2.tsx index b7bfa5bd4..009122122 100644 --- a/apps/webapp/components/_wip/landing-page2.tsx +++ b/apps/webapp/components/_wip/landing-page2.tsx @@ -13,7 +13,7 @@ export function LandingPage2({ content }: LandingPage2Props) {

    {content.heading}

    -

    +

    {content.description}

    diff --git a/apps/webapp/components/_wip/tokenization-landing.tsx b/apps/webapp/components/_wip/tokenization-landing.tsx index 3bfa23f57..0304a29ba 100644 --- a/apps/webapp/components/_wip/tokenization-landing.tsx +++ b/apps/webapp/components/_wip/tokenization-landing.tsx @@ -15,7 +15,7 @@ export function TokenizationLanding() {

    {section.title}

    -

    +

    {section.description}

    @@ -27,9 +27,7 @@ export function TokenizationLanding() {
  • {detail.header}

    -

    - {detail.content} -

    +

    {detail.content}

  • ))} diff --git a/apps/webapp/components/dialogs/register.tsx b/apps/webapp/components/dialogs/register.tsx index 77ced29e3..854fac523 100644 --- a/apps/webapp/components/dialogs/register.tsx +++ b/apps/webapp/components/dialogs/register.tsx @@ -9,7 +9,7 @@ import { DialogTitle, } from '@/components/ui/dialog' import { useSession } from '@/hooks/use-session' -import { runtimeEnv } from 'app-lib' +import { runtimeEnv } from '@repo/utils' import { useRouter } from 'next/navigation' import QRCode from 'react-qr-code' import { useEffectOnce } from 'react-use' diff --git a/apps/webapp/components/layout/footer/footer.tsx b/apps/webapp/components/layout/footer/footer.tsx index 486bd3053..dcc4b06da 100644 --- a/apps/webapp/components/layout/footer/footer.tsx +++ b/apps/webapp/components/layout/footer/footer.tsx @@ -1,11 +1,6 @@ -import { LearnSection } from '@/components/layout/footer/learn-section' import { getDictionary } from '@/dictionaries' import type { Lang } from '@/dictionaries/locales' -import { appConfig } from '@/lib/config' import dynamic from 'next/dynamic' -import { FAQ } from './faq' -import Participate from './participate' -import { RecentArticles } from './recent' const DynamicNewsletter = dynamic(() => import('./newsletter') as any, { ssr: false, @@ -15,19 +10,6 @@ export default async function Footer({ params }: { params: { lang: Lang } }) { const dict = await getDictionary(params.lang) return (
    -
    - {/* Call to action, how it works sections */} - - {appConfig.features.learn ? ( - <> - - - ) : null} - - - {/* Media and other */} - -
    ) diff --git a/apps/webapp/components/layout/footer/learn-section.tsx b/apps/webapp/components/layout/footer/learn-section.tsx deleted file mode 100644 index 8e222a0c1..000000000 --- a/apps/webapp/components/layout/footer/learn-section.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import Image from 'next/image' -import Link from 'next/link' -import { Section } from '../../shared/section' - -export function LearnSection() { - return ( -
    -
    -
    - - -
    - - - - {content.cards[0].title} - - - - {content.cards[0].title} - -
    - - -
    - {content.cards[0].paragraphs.map((p) => ( -

    - {p} -

    - ))} -
    -
    -
    - - - -
    - - -
    - - - {content.cards[1].title} - -
    - - - - - {content.cards[1].title} - -
    - - - - -
    - - - {content.cards[2].title} - -
    - - - - - {content.cards[2].title} - -
    - - -
    -
    -
    - ) -} - -const content = { - sectionTitle: 'Learn More About Bitlauncher', - cards: [ - { - title: 'How to Participate in a Bitlauncher IDO?', - paragraphs: [ - "A good place to start is: what is Bitlauncher? (We'll give you the brief version). Bitlauncher is a platform that connects young projects with early community members through initial decentralized offerings (IDOS). It provides a unique opportunity for contributors to engage with up-and-coming tech innovations.", - 'By participating in an IDO on Bitlauncher, users can gain early access to tokens from new blockchain projects. This early access can potentially lead to significant benefits if the projects grow in value and popularity. Bitlauncher aims to democratize the investment process, making it accessible to a broader audience.', - ], - images: [ - '/images/home/platform-circles.webp', - '/images/home/platform-circles.png', - ], - }, - { - title: 'How to buy the Bitlauncher $BL token?', - paragraphs: [], - images: ['/images/home/bl-coins.webp', '/images/home/bl-coins.png'], - }, - { - title: 'What is an IDO (Initial Decentralized Offering)?', - paragraphs: [], - images: ['/images/home/glass.webp', '/images/home/glass.png'], - }, - ], -} diff --git a/apps/webapp/components/layout/footer/newsletter.tsx b/apps/webapp/components/layout/footer/newsletter.tsx index f29db7daf..cdb8e6ce6 100644 --- a/apps/webapp/components/layout/footer/newsletter.tsx +++ b/apps/webapp/components/layout/footer/newsletter.tsx @@ -89,7 +89,7 @@ export default function Newsletter({ lang }: LangProp) { return (
    -
    +
    -

    - Sign up for our newsletter -

    +

    Sign up for our newsletter

    To stay up to date with our progress, announcements and exclusive discounts, sign up with your email below: diff --git a/apps/webapp/components/layout/header/bitcash-access.tsx b/apps/webapp/components/layout/header/bitcash-access.tsx index e530308af..70da8027e 100644 --- a/apps/webapp/components/layout/header/bitcash-access.tsx +++ b/apps/webapp/components/layout/header/bitcash-access.tsx @@ -7,11 +7,10 @@ import { IconDownRightArrow } from '@/components/ui/icons' import { useErc20Balance } from '@/hooks/use-balance' import { useSession } from '@/hooks/use-session' import { cn } from '@/lib/utils' -import { TestnetUSDCred } from 'app-contracts' -import { runtimeEnv } from 'app-lib' +import { TestnetUSDCred } from '@repo/contracts' +import { runtimeEnv } from '@repo/utils' import type { VariantProps } from 'class-variance-authority' import { LucideWallet } from 'lucide-react' -import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' import { useEffect, useState } from 'react' // import { bitcashLogin } from '@/lib/esr' diff --git a/apps/webapp/components/layout/header/index.tsx b/apps/webapp/components/layout/header/index.tsx index 3d8c90c5a..bdb2ecddd 100644 --- a/apps/webapp/components/layout/header/index.tsx +++ b/apps/webapp/components/layout/header/index.tsx @@ -14,28 +14,28 @@ import { Navigation } from './new-nav' export function Header({ lang, dict }: HeaderProps) { return ( -

    -
    -
    +
    +
    + {/* Left Section with Icon */} +
    -
    + {/* Center Section (Navigation Links) */} +
    {/* // ? Development only */} {appConfig.features.newNavStruct ? ( ) : ( - // ? Production only until development of newNavStruct is complete (UI/UX + content upt) )}
    - {/* Desktop action buttons */} -
    + {/* Right Section (Buttons/Language Selector) */} +
    - {/* */} Login}> @@ -46,7 +46,7 @@ export function Header({ lang, dict }: HeaderProps) {
    -
    +
    ) } diff --git a/apps/webapp/components/layout/header/nav-links.tsx b/apps/webapp/components/layout/header/nav-links.tsx index 2c3ad6307..1f5243df7 100644 --- a/apps/webapp/components/layout/header/nav-links.tsx +++ b/apps/webapp/components/layout/header/nav-links.tsx @@ -5,7 +5,7 @@ import { useSession } from '@/hooks/use-session' import { appConfig } from '@/lib/config' import type { LangProp } from '@/types/routing.type' import { useAccountModal, useConnectModal } from '@rainbow-me/rainbowkit' -import { formatAddress } from 'app-lib' +import { formatAddress } from '@repo/utils' import Link from 'next/link' import { useRouter } from 'next/navigation' import { v4 as uuidv4 } from 'uuid' diff --git a/apps/webapp/components/layout/providers.tsx b/apps/webapp/components/layout/providers.tsx index 13aaeed6e..95265c7e3 100644 --- a/apps/webapp/components/layout/providers.tsx +++ b/apps/webapp/components/layout/providers.tsx @@ -5,6 +5,7 @@ import { MobileNavProvider } from '@/hooks/use-mobile-navigation' import { SessionProvider } from '@/hooks/use-session' import { UseSigningRequestProvider } from '@/hooks/use-signing-request' import { appConfig } from '@/lib/config' +import multibase, { MultibaseProvider } from '@multibase/js' import { RainbowKitProvider, type Theme, @@ -94,6 +95,17 @@ const customRainbowKitTheme = merge(lightTheme(), { // } } as Theme) +if (typeof window !== 'undefined') { + const multibaseKey = appConfig.multibase.key + + if (!multibaseKey) { + console.error('Missing MULTIBASE_API_KEY') + } else { + multibase.init(multibaseKey) + console.info('Multibase Initialized') + } +} + export function Providers({ children, ...props }: ThemeProviderProps) { return ( @@ -108,11 +120,13 @@ export function Providers({ children, ...props }: ThemeProviderProps) { appName: 'Bitlauncher', }} > - - - {children} - - + + + + {children} + + + diff --git a/apps/webapp/components/layout/footer/recent.tsx b/apps/webapp/components/layout/section/article-section.tsx similarity index 100% rename from apps/webapp/components/layout/footer/recent.tsx rename to apps/webapp/components/layout/section/article-section.tsx diff --git a/apps/webapp/components/layout/footer/faq.tsx b/apps/webapp/components/layout/section/faq-section.tsx similarity index 69% rename from apps/webapp/components/layout/footer/faq.tsx rename to apps/webapp/components/layout/section/faq-section.tsx index cb1047870..ad8dc1409 100644 --- a/apps/webapp/components/layout/footer/faq.tsx +++ b/apps/webapp/components/layout/section/faq-section.tsx @@ -1,11 +1,11 @@ -import { Section } from '@/components/shared/section' +import { Section } from '@/components/shared/section'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, -} from '@/components/ui/accordion' -import type { LangProp } from '@/types/routing.type' +} from '@/components/ui/accordion'; +import type { LangProp } from '@/types/routing.type'; export function FAQ({ lang, dict }: FAQProps) { return ( @@ -19,10 +19,10 @@ export function FAQ({ lang, dict }: FAQProps) { {dict.faq.questions.map( (item: { question: string; answer: string }, index: number) => ( - + {item.question} - + {item.answer} diff --git a/apps/webapp/components/layout/section/learn-section.tsx b/apps/webapp/components/layout/section/learn-section.tsx new file mode 100644 index 000000000..c3075b182 --- /dev/null +++ b/apps/webapp/components/layout/section/learn-section.tsx @@ -0,0 +1,119 @@ +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import Image from 'next/image' +import Link from 'next/link' +import { Section } from '../../shared/section' + +export function LearnSection() { + const mainCard = content.cards[0] + const secondaryCards = content.cards.filter((_, i) => i > 0) + return ( +
    +
    + + +
    + + + + {mainCard.title} + + + + {mainCard.title} + +
    + + +
    + {mainCard.paragraphs.map((p, i) => ( +

    + {p} +

    + ))} +
    +
    +
    + + + +
    + {secondaryCards.map((card, i) => ( + + +
    + + + {card.title} + +
    + + + + + {card.title} + +
    + + + ))} +
    +
    +
    + ) +} + +const content = { + sectionTitle: 'Learn More About Bitlauncher', + cards: [ + { + title: 'How to Partcipate in the Bitlauncher Presale?', + paragraphs: [ + "A good place to start is: what is Bitlauncher? (We'll give you the brief version). Bitlauncher is a platform that connects young projects with early community members through initial decentralized offerings (IDOS). It provides a unique opportunity for contributors to engage with up-and-coming tech innovations.", + 'By participating in an IDO on Bitlauncher, users can gain early access to tokens from new blockchain projects. This early access can potentially lead to significant benefits if the projects grow in value and popularity. Bitlauncher aims to democratize the investment process, making it accessible to a broader audience.', + ], + images: [ + '/images/home/platform-circles.webp', + '/images/home/platform-circles.png', + ], + articleLink: + '/blog/bitlauncher/welcome-to-the-bitlauncher-presale-starting-august-31st', + }, + { + title: 'What is the Bitlauncher ($BL) Token?', + paragraphs: [], + images: ['/images/home/bl-coins.webp', '/images/home/bl-coins.png'], + articleLink: '/blog/bitlauncher/what-is-the-bitlauncher-bl-token', + }, + { + title: 'What is a Public Token Presale?', + paragraphs: [], + images: ['/images/home/glass.webp', '/images/home/glass.png'], + articleLink: '/blog/bitlauncher/what-is-a-public-token-presale', + }, + ], +} diff --git a/apps/webapp/components/layout/footer/short-video.tsx b/apps/webapp/components/layout/section/short-video-section.tsx similarity index 100% rename from apps/webapp/components/layout/footer/short-video.tsx rename to apps/webapp/components/layout/section/short-video-section.tsx diff --git a/apps/webapp/components/layout/footer/participate.tsx b/apps/webapp/components/layout/section/steps-section.tsx similarity index 79% rename from apps/webapp/components/layout/footer/participate.tsx rename to apps/webapp/components/layout/section/steps-section.tsx index 919662aef..a3603b899 100644 --- a/apps/webapp/components/layout/footer/participate.tsx +++ b/apps/webapp/components/layout/section/steps-section.tsx @@ -8,7 +8,7 @@ import type { Lang } from '@/dictionaries/locales' import { cn } from '@/lib/utils' import Link from 'next/link' -export default function Participate({ lang, dict }: ParticipateProps) { +export default function StepsSection({ lang, dict }: StepsSectionProps) { return (
    (
    -

    +

    {step.title}

    -

    +

    {step.description}

    {index > 0 ? ( @@ -60,7 +60,7 @@ export default function Participate({ lang, dict }: ParticipateProps) { ) } -export interface ParticipateProps { +export interface StepsSectionProps { dict: any lang: Lang } diff --git a/apps/webapp/components/layout/session/register-dialog-content.tsx b/apps/webapp/components/layout/session/register-dialog-content.tsx index 342fd8466..08f2bea79 100644 --- a/apps/webapp/components/layout/session/register-dialog-content.tsx +++ b/apps/webapp/components/layout/session/register-dialog-content.tsx @@ -9,7 +9,7 @@ import { DialogTitle, } from '@/components/ui/dialog' import { useSession } from '@/hooks/use-session' -import { runtimeEnv } from 'app-lib' +import { runtimeEnv } from '@repo/utils' import { useRouter } from 'next/navigation' import QRCode from 'react-qr-code' import { useEffectOnce } from 'react-use' diff --git a/apps/webapp/components/layout/session/session-button.tsx b/apps/webapp/components/layout/session/session-button.tsx index 9c6e6d3bf..20122c545 100644 --- a/apps/webapp/components/layout/session/session-button.tsx +++ b/apps/webapp/components/layout/session/session-button.tsx @@ -7,10 +7,9 @@ import { PopoverTrigger, } from '@/components/ui/popover' import { useSession } from '@/hooks/use-session' -import { appConfig } from '@/lib/config' import { cn } from '@/lib/utils' import { useAccountModal } from '@rainbow-me/rainbowkit' -import { formatAddress } from 'app-lib' +import { formatAddress } from '@repo/utils' import { User, Wallet } from 'lucide-react' import { useRouter } from 'next/navigation' import { isMobile } from 'react-device-detect' diff --git a/apps/webapp/components/routes/about/about-bitlauncher/index.tsx b/apps/webapp/components/routes/about/about-bitlauncher/index.tsx index 096a49bae..97dff2723 100644 --- a/apps/webapp/components/routes/about/about-bitlauncher/index.tsx +++ b/apps/webapp/components/routes/about/about-bitlauncher/index.tsx @@ -1,8 +1,11 @@ +import { LearnSection } from '@/components/layout/section/learn-section' +import StepsSection from '@/components/layout/section/steps-section' import { Banner } from '@/components/shared/banner' import { CommunityCard } from '@/components/shared/community-card' import { getDictionary } from '@/dictionaries' import type { Lang } from '@/dictionaries/locales' import Image from 'next/image' +import Balancer from 'react-wrap-balancer' export async function AboutBitlauncherPageLanding({ content, @@ -12,37 +15,21 @@ export async function AboutBitlauncherPageLanding({ const dict = await getDictionary(lang) return ( <> -
    -
    +
    +
    -

    {content.title}

    -
    - Bitlauncher is a pioneering launchpad dedicated to - transforming the landscape of artificial intelligence (AI) and - cryptocurrency. We are on a mission to empower the next wave of AI - innovation by providing open-source AI projects with equitable - fundraising opportunities and decentralized organization through - the use of blockchain technology. +

    + {' '} + {content.title} +

    +
    + {content.paragraph}

    - At Bitlauncher, we{' '} - combine the transformative powers of AI and cryptocurrency{' '} - to address the unique challenges faced by AI startups. By - integrating tokenization and decentralized autonomous - organizations (DAOs), we create a seamless synergy that enables - these startups to overcome funding barriers, accelerate their - growth, and harness global resources. -
    -
    - Our platform is built on a foundation of{' '} - - transparency, inclusivity, and community-driven progress - - . We foster a collaborative environment where developers, - contributors, and AI enthusiasts can come together to share - resources, exchange ideas, and shape the future of technology. + {content.paragraph2}

    + {content.paragraph3}
    @@ -51,8 +38,10 @@ export async function AboutBitlauncherPageLanding({
    + + +
    - ) } @@ -66,7 +55,9 @@ interface Image { export interface AboutBitlauncherPageContent { title: string - description: string + paragraph: React.ReactNode + paragraph2: React.ReactNode + paragraph3: React.ReactNode image: Image } diff --git a/apps/webapp/components/routes/blog/article/index.tsx b/apps/webapp/components/routes/blog/article/index.tsx index 70a5c657a..53c507c70 100644 --- a/apps/webapp/components/routes/blog/article/index.tsx +++ b/apps/webapp/components/routes/blog/article/index.tsx @@ -1,6 +1,5 @@ import { LazyImage } from '@/components/shared/lazy-image' import { Tag } from '@/components/shared/tag' -import { Button } from '@/components/ui/button' import type { Lang } from '@/dictionaries/locales' import { readingTime } from '@/lib/blog' import { cn } from '@/lib/utils' @@ -8,7 +7,6 @@ import type { BlogArticleRecord } from '@/services/datocms' import { render as toPlainText } from 'datocms-structured-text-to-plain-text' import { isHeading, isParagraph } from 'datocms-structured-text-utils' import type { StaticImport } from 'next/dist/shared/lib/get-img-props' -import Image from 'next/image' import Link from 'next/link' import { Suspense } from 'react' import { @@ -75,180 +73,188 @@ export function BlogPage({ } }) return ( -
    -
    -
    -
    -

    - {title} -
    -

    - - {blogContent.description} - -
    +
    +
    +
    +

    + {title} +
    +

    +

    + {blogContent.description} +

    +
    -
    - - {blogContent.authorName} - - - {new Date(blogContent._publishedAt).toLocaleDateString('en-US', { - month: 'short', - day: '2-digit', - year: 'numeric', - })}{' '} - ∙ {readingTime(blogContent)} min read - +
    + + {blogContent.authorName} + + + {new Date(blogContent._publishedAt).toLocaleDateString('en-US', { + month: 'short', + day: '2-digit', + year: 'numeric', + })}{' '} + ∙ {readingTime(blogContent)} min read + -
    - {blogContent.topics?.map((topic, index) => ( - - ))} -
    +
    + {blogContent.topics?.map((topic, index) => ( + + ))}
    +
    -
    -
    - {blogContent?.contentBlock?.map( - ({ mainContent, topImages }, ind: number) => { - // if (ind >= 2) return null +
    +
    + {blogContent?.contentBlock?.map( + ({ mainContent, topImages }, ind: number) => { + // if (ind >= 2) return null - mainContent.value.document.children = - mainContent.value.document.children.map((item) => { - if (item.type !== 'paragraph') return item + mainContent.value.document.children = + mainContent.value.document.children.map((item) => { + if (item.type !== 'paragraph') return item - const sanitizedChildren = item.children?.map((child) => { - if (child.type !== 'span') return child - if (typeof child.value === 'string') return child + const sanitizedChildren = item.children?.map((child) => { + if (child.type !== 'span') return child + if (typeof child.value === 'string') return child - if (Array.isArray(child.value)) { - return { - ...child, - value: (child.value as string[]).join(' '), - } + if (Array.isArray(child.value)) { + return { + ...child, + value: (child.value as string[]).join(' '), } - return child - }) - return { ...item, children: sanitizedChildren } - }) as any + } + return child + }) + return { ...item, children: sanitizedChildren } + }) as any - // console.log(`================ ${ind} =================`) - // console.log( - // JSON.stringify(mainContent.value.document.children) - // ) - return ( -
    - {topImages.map( - ( - image: { url: string | StaticImport; alt: string }, - index, - ) => ( -
    - {/* {image?.alt */} - -
    - ), - )} -
    - {/* { mainContent.value.document.children.values} */} - { - const HeadingTag = `h${node.level}` as any - const anchor = toPlainText(node) - ?.trim() - .toLowerCase() - .replace(/ /g, '-') - .replace(/[^\w-]+/g, '') - .replace(/-$/, '') - return ( - // add types to ref and key props to satisfy React requirements - - {children} - - ) - }, - ), - renderNodeRule(isParagraph, ({ children, key }) => { + // console.log(`================ ${ind} =================`) + // console.log( + // JSON.stringify(mainContent.value.document.children) + // ) + return ( +
    + {topImages.map( + ( + image: { url: string | StaticImport; alt: string }, + index, + ) => ( +
    + {/* {image?.alt */} + +
    + ), + )} +
    + {/* { mainContent.value.document.children.values} */} + { + const HeadingTag = `h${node.level}` as any + const anchor = toPlainText(node) + ?.trim() + .toLowerCase() + .replace(/ /g, '-') + .replace(/[^\w-]+/g, '') + .replace(/-$/, '') return ( -

    + // add types to ref and key props to satisfy React requirements + {children} -

    + ) - }), - ]} - /> -
    + }, + ), + renderNodeRule(isParagraph, ({ children, key }) => { + return ( +

    + {children} +

    + ) + }), + ]} + />
    - ) - }, - )} -
    +
    + ) + }, + )} +
    -
    - +
    + -
    - - Share this article - - Loading...
    }> - - -
    +
    + + Share this article + + Loading...
    }> + +
    -
    +
    {relatedBlogs.length > 0 && ( -
    -
    +
    +
    /Related stories See All Stories >
    -
      - {relatedBlogs?.slice(0, 5).map((post) => ( +
        + {relatedBlogs?.slice(0, 5).map((post, index) => ( ))}
    )} -
    +
    ) } diff --git a/apps/webapp/components/routes/blog/blog-sections.tsx b/apps/webapp/components/routes/blog/blog-sections.tsx index ff741d476..9c233c471 100644 --- a/apps/webapp/components/routes/blog/blog-sections.tsx +++ b/apps/webapp/components/routes/blog/blog-sections.tsx @@ -39,7 +39,7 @@ export function BlogSections({
    -
    +
      {section?.articles // ?.slice(0, 4) ?.map((post, index) => ( @@ -49,9 +49,13 @@ export function BlogSections({ key={post.id + '-' + index} lang={lang} meta={true} + className={cn( + index === 3 ? 'md:hidden xl:block' : '', + index === 4 ? 'lg:hidden 2xl:block' : '', + )} /> ))} -
    +
    ), )} diff --git a/apps/webapp/components/routes/blog/category.tsx b/apps/webapp/components/routes/blog/category.tsx index e45e75c88..20f079e87 100644 --- a/apps/webapp/components/routes/blog/category.tsx +++ b/apps/webapp/components/routes/blog/category.tsx @@ -95,6 +95,10 @@ export function CategoryComponent({ params, sections }: BlogCategoryPageProps) { key={index} lang={params.lang} meta={true} + className={cn( + index === 3 ? 'md:hidden xl:block' : '', + index === 4 ? 'lg:hidden 2xl:block' : '', + )} /> ))} diff --git a/apps/webapp/components/routes/blog/hero-section/index.tsx b/apps/webapp/components/routes/blog/hero-section/index.tsx index 630c185f7..5de90017d 100644 --- a/apps/webapp/components/routes/blog/hero-section/index.tsx +++ b/apps/webapp/components/routes/blog/hero-section/index.tsx @@ -20,7 +20,7 @@ export function HeroSection({ recent, lang }: HeroSectionProps) { ), )} diff --git a/apps/webapp/components/routes/home/auction-card.tsx b/apps/webapp/components/routes/home/auction-card.tsx index 87e723b46..cf42d84ec 100644 --- a/apps/webapp/components/routes/home/auction-card.tsx +++ b/apps/webapp/components/routes/home/auction-card.tsx @@ -2,7 +2,7 @@ import type { Project } from '@/lib/projects' import { cn } from '@/lib/utils' import Image from 'next/image' import Link from 'next/link' -import React, { Suspense } from 'react' +import { Suspense } from 'react' import { isMobile } from 'react-device-detect' import Balancer from 'react-wrap-balancer' import { AuctionCardButtons } from './auction-card-buttons' @@ -29,7 +29,7 @@ export function AuctionCard({ const isFutureOrComingAuction = badgeText.match(/(FUTURE|COMING SOON)/) return ( -
    +
    {badgeText}}> - + -
    +

    {title}

    -

    {pitch}

    +

    {pitch}

      diff --git a/apps/webapp/components/routes/home/features.tsx b/apps/webapp/components/routes/home/features.tsx index 74be7116f..f8211ac30 100644 --- a/apps/webapp/components/routes/home/features.tsx +++ b/apps/webapp/components/routes/home/features.tsx @@ -29,7 +29,7 @@ export function Features({ lang, dict }: FeaturesProps) {

      {content.title}

      -

      +

      {content.description}

    diff --git a/apps/webapp/components/routes/home/hero/index.tsx b/apps/webapp/components/routes/home/hero/index.tsx index 18770ead4..1221929e3 100644 --- a/apps/webapp/components/routes/home/hero/index.tsx +++ b/apps/webapp/components/routes/home/hero/index.tsx @@ -10,12 +10,14 @@ import Image from 'next/image' import { Suspense } from 'react' import Balancer from 'react-wrap-balancer' +// ? Hero must be always minimum of 90vh and reducing it by coyunting the height of the header. +// ? This way user would be able to see a hint of the next section. export function NewHomeHero() { return ( -
    -
    +
    +
    -

    +

    EARLY ACCESS TO
    THE NEXT GLOBAL
    @@ -33,7 +35,7 @@ export function NewHomeHero() { className="relative object-contain h-[400px] w-[400px]" />

    -
    +
    { + label: string + className?: string +} + +export function MotionFigcaption({ + label, + color, + size, + className, +}: MotionFigcaptionProps) { return ( {label} diff --git a/apps/webapp/components/routes/home/why-choose-us.tsx b/apps/webapp/components/routes/home/why-choose-us.tsx index 331264efc..5b6b792ef 100644 --- a/apps/webapp/components/routes/home/why-choose-us.tsx +++ b/apps/webapp/components/routes/home/why-choose-us.tsx @@ -8,22 +8,24 @@ export function WhyChooseUs({ lang, dict }: WhyChooseUsProps) { const content = dict.whyChooseUs return (
    -
    +
      {content.features.map((feature: Feature) => { const IconComponent = Icons[feature.icon] as React.ElementType return ( - - -

      - {feature.title} -

      -

      - {feature.description} -

      -
      +
    • + + +

      + {feature.title} +

      +

      + {feature.description} +

      +
      +
    • ) })} -
    +
    ) } diff --git a/apps/webapp/components/routes/project/auction/auction-bids.tsx b/apps/webapp/components/routes/project/auction/auction-bids.tsx index 8ba02b31c..d4c2d1f0c 100644 --- a/apps/webapp/components/routes/project/auction/auction-bids.tsx +++ b/apps/webapp/components/routes/project/auction/auction-bids.tsx @@ -10,10 +10,10 @@ import { } from '@/components/ui/table' import type { ProjectWithAuction } from '@/lib/projects' import { cn } from '@/lib/utils' +import { TestnetEasyAuction, TestnetUSDCred } from '@repo/contracts' +import { toSmallestUnit } from '@repo/utils' import { readContract, writeContract } from '@wagmi/core' import { erc20Abi } from 'abitype/abis' -import { TestnetEasyAuction, TestnetUSDCred } from 'app-contracts' -import { toSmallestUnit } from 'app-lib' import { useEffect, useState } from 'react' import toast from 'react-hot-toast' import type { Address } from 'viem' diff --git a/apps/webapp/components/routes/project/auction/auction-orders.tsx b/apps/webapp/components/routes/project/auction/auction-orders.tsx index 077a35d5f..7e9952677 100644 --- a/apps/webapp/components/routes/project/auction/auction-orders.tsx +++ b/apps/webapp/components/routes/project/auction/auction-orders.tsx @@ -8,8 +8,8 @@ import { TableRow, } from '@/components/ui/table' import { useSupabaseClient } from '@/services/supabase' -import { TestnetEasyAuction } from 'app-contracts' -import { formatAddress } from 'app-lib' +import { TestnetEasyAuction } from '@repo/contracts' +import { formatAddress } from '@repo/utils' import BN from 'bn.js' import { format } from 'date-fns' import Link from 'next/link' diff --git a/apps/webapp/components/routes/project/copy-shorlink.tsx b/apps/webapp/components/routes/project/copy-shorlink.tsx index 0fc4ca3d9..1c59cfb0e 100644 --- a/apps/webapp/components/routes/project/copy-shorlink.tsx +++ b/apps/webapp/components/routes/project/copy-shorlink.tsx @@ -1,21 +1,17 @@ 'use client' -import { AnimatePresence } from 'framer-motion' -import { LucideCheck, LucideLoader2, LucideShare, LucideX } from 'lucide-react' -import { useState } from 'react' - import { generateShortLink } from '@/actions' import { useSession } from '@/hooks/use-session' import { useSupabaseClient } from '@/services/supabase/client' -import { uniq } from 'lodash' +import { AnimatePresence } from 'framer-motion' +import { LucideCheck, LucideLoader2, LucideShare, LucideX } from 'lucide-react' import { useParams } from 'next/navigation' -import { useAccount } from 'wagmi' +import { useState } from 'react' export function CopyShortlinkIcon() { const [status, setStatus] = useState< 'default' | 'loading' | 'copied' | 'error' >('default') const { session } = useSession() - const { address } = useAccount() const supabase = useSupabaseClient() const existingParams = useParams() // Get existing query parameters const param = session @@ -24,11 +20,10 @@ export function CopyShortlinkIcon() { const checkShareLink = async () => { const { data, error } = await supabase - .from('user') + .from('account') // select shareLink from user where linkPath = 'https://bitlauncher.ai${window.location.pathname}${param}' - .select('id, account, address, short_link') + .select('account, short_link') .eq('account', session?.account || '') - .contains('address', [address || '']) .single() if (error) { @@ -43,27 +38,19 @@ export function CopyShortlinkIcon() { if (!data.short_link) { const { data: dubCoShortLink, error: dubCoError } = await generateShortLink( - 'https://bitlauncher.ai' + window.location.pathname + param, + `https://bitlauncher.ai${window.location.pathname}${param}`, ) if (dubCoError) { - console.error('❌ Failed to check share link:', dubCoError) + console.error('❌ Failed to generate short link:', dubCoError) setStatus('error') return { data: null, error: dubCoError } } - // ? Doing upsert (account, address) for current users with active sessions - const updatedAddresses = [ - ...(data?.address.length - ? [...data?.address, address as string] - : [address as string]), - ] - await supabase.from('user').upsert( + await supabase.from('account').upsert( { - id: data.id, account: session?.account || '', - address: uniq(updatedAddresses), short_link: dubCoShortLink?.shortLink, }, { @@ -97,6 +84,8 @@ export function CopyShortlinkIcon() { return (
    ) } - -const tokens = - appConfig.env === 'dev' ? ['USDT', 'BITUSD'] : ['USDT', 'USDC', 'BITUSD'] diff --git a/apps/webapp/components/routes/project/presale/presale-transactions-card.tsx b/apps/webapp/components/routes/project/presale/presale-transactions-card.tsx index f071a13bc..34e9b21bd 100644 --- a/apps/webapp/components/routes/project/presale/presale-transactions-card.tsx +++ b/apps/webapp/components/routes/project/presale/presale-transactions-card.tsx @@ -15,13 +15,10 @@ import { TableHeader, TableRow, } from '@/components/ui/table' -import { appConfig } from '@/lib/config' import { useSupabaseClient } from '@/services/supabase' import type { Tables } from '@repo/supabase' -import { formatAddress } from 'app-lib' import { useEffect, useState } from 'react' import { useAccount } from 'wagmi' -import { TestnetBLPL } from '../../../../../../packages/app-contracts/src/dev/tokens/testnet-blpl' export function PresaleTransactionsCard() { const { address } = useAccount() @@ -77,18 +74,18 @@ export function PresaleTransactionsCard() { ...prev, ]) } else if (payload.eventType === 'UPDATE') { - setTransactions((prev) => - prev - .map((t) => + setTransactions( + (prev) => + prev.map((t) => t.trx_hash === payload.new.trx_hash ? (payload.new as Tables<'transfer'>) : t, - ) - .sort( - (a, b) => - new Date(b.created_at).getTime() - - new Date(a.created_at).getTime(), ), + // .sort( + // (a, b) => + // new Date(b.created_at).getTime() - + // new Date(a.created_at).getTime(), + // ), ) } else if (payload.eventType === 'DELETE') { setTransactions((prev) => @@ -157,59 +154,60 @@ export function PresaleTransactionsCard() { } function TransactionRow({ transaction }: { transaction: Tables<'transfer'> }) { - const chain = transaction.chain_id - ? appConfig.chains.get(transaction.chain_id) - : null - return ( - - -
    - {formatAddress(transaction.from ?? '')} -
    -
    + return <> + // const chain = transaction.chain_id + // ? appConfig.chains.get(transaction.chain_id) + // : null + // return ( + // + // + //
    + // {formatAddress(transaction.from ?? '')} + //
    + //
    - - {transaction.amount !== null - ? (transaction.amount / 1000000).toFixed(6) - : 'N/A'} - + // + // {transaction.amount !== null + // ? (transaction.amount / 1000000).toFixed(6) + // : 'N/A'} + // - - {chain?.blockExplorers?.default ? ( - - {formatAddress(transaction.trx_hash)} - - ) : ( - formatAddress(transaction.trx_hash) - )} - + // + // {chain?.blockExplorers?.default ? ( + // + // {formatAddress(transaction.trx_hash)} + // + // ) : ( + // formatAddress(transaction.trx_hash) + // )} + // - - {TestnetBLPL.chain?.blockExplorers?.default && - transaction.bl_presale_trx ? ( - - {formatAddress(transaction.bl_presale_trx)} - - ) : transaction.bl_presale_trx ? ( - formatAddress(transaction.bl_presale_trx) - ) : ( - 'pending' - )} - - - {new Date(transaction.created_at).toLocaleDateString()} - - {chain?.name} -
    - ) + // + // {TestnetBLPL.chain?.blockExplorers?.default && + // transaction.bl_presale_trx ? ( + // + // {formatAddress(transaction.bl_presale_trx)} + // + // ) : transaction.bl_presale_trx ? ( + // formatAddress(transaction.bl_presale_trx) + // ) : ( + // 'pending' + // )} + // + // + // {new Date(transaction.created_at).toLocaleDateString()} + // + // {chain?.name} + //
    + // ) } diff --git a/apps/webapp/components/routes/project/project-header.tsx b/apps/webapp/components/routes/project/project-header.tsx index 423a43160..d2498deee 100644 --- a/apps/webapp/components/routes/project/project-header.tsx +++ b/apps/webapp/components/routes/project/project-header.tsx @@ -30,7 +30,7 @@ export function ProjectHeader({ {project.pitch} {project.title.toUpperCase()} diff --git a/apps/webapp/components/routes/project/project-pills.tsx b/apps/webapp/components/routes/project/project-pills.tsx index 55bc08217..fa0f485e3 100644 --- a/apps/webapp/components/routes/project/project-pills.tsx +++ b/apps/webapp/components/routes/project/project-pills.tsx @@ -24,7 +24,7 @@ export function ProjectPills({ project }: { project: Project }) { ] return ( -
    +
    {links.map((l, index) => (
    ) } diff --git a/apps/webapp/components/shared/content.tsx b/apps/webapp/components/shared/content.tsx index 259fcd699..53aa35cb4 100644 --- a/apps/webapp/components/shared/content.tsx +++ b/apps/webapp/components/shared/content.tsx @@ -20,7 +20,7 @@ export function PageContent({ data }: { data: PageContentData }) { > {item.text} -
    +
    ) diff --git a/apps/webapp/components/shared/countdown.tsx b/apps/webapp/components/shared/countdown.tsx index ddeb85279..d8b3f7d1d 100644 --- a/apps/webapp/components/shared/countdown.tsx +++ b/apps/webapp/components/shared/countdown.tsx @@ -1,9 +1,12 @@ 'use client' -import { useEffect, useState } from 'react' +import { differenceInSeconds } from 'date-fns'; +import { useEffect, useState } from 'react'; -export function Countdown() { - const targetDate = new Date('July 30, 2024') +export function Countdown({ + targetDate, + heading, +}: { targetDate: Date; heading: string }) { const [timeLeft, setTimeLeft] = useState({ days: '00', hours: '00', @@ -14,39 +17,36 @@ export function Countdown() { useEffect(() => { const interval = setInterval(() => { const now = new Date() - const timeDiff = targetDate.getTime() - now.getTime() + const timeDiff = differenceInSeconds(targetDate, now) if (timeDiff > 0) { - const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24)) + const days = Math.floor(timeDiff / (24 * 60 * 60)) .toString() .padStart(2, '0') - const hours = Math.floor( - (timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60), - ) + const hours = Math.floor((timeDiff % (24 * 60 * 60)) / (60 * 60)) .toString() .padStart(2, '0') - const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60)) - .toString() - .padStart(2, '0') - const seconds = Math.floor((timeDiff % (1000 * 60)) / 1000) + const minutes = Math.floor((timeDiff % (60 * 60)) / 60) .toString() .padStart(2, '0') + const seconds = (timeDiff % 60).toString().padStart(2, '0') setTimeLeft({ days, hours, minutes, seconds }) } else { clearInterval(interval) + setTimeLeft({ days: '00', hours: '00', minutes: '00', seconds: '00' }) } }, 1000) return () => clearInterval(interval) - }, []) + }, [targetDate]) return (

    {/* */} - Pre-Sale Countdown + {heading}

    diff --git a/apps/webapp/components/shared/media-card.tsx b/apps/webapp/components/shared/media-card.tsx index 41a52bcf5..4082dd4a1 100644 --- a/apps/webapp/components/shared/media-card.tsx +++ b/apps/webapp/components/shared/media-card.tsx @@ -1,6 +1,6 @@ import { VideoDialog } from '@/components/dialogs/video-dialog' import { Card, CardContent } from '@/components/ui/card' -import { cn } from '@/lib/utils' // Import the utility function +import { cn } from '@/lib/utils'; // Import the utility function import type { YouTubePlaylistItem } from '@/services/youtube/index' import type { LangProp } from '@/types/routing.type' import Image from 'next/image' @@ -12,30 +12,27 @@ export function MediaCard({ video, lang, className }: MediaCardProps) { lang={lang} video={video} trigger={ - -
    -
    - {video.snippet.title} -
    -
    - -

    - {video.snippet.title} -

    -
    -
    +
  • + +
    +
    + {video.snippet.title} +
    +
    + +

    + {video.snippet.title} +

    +
    +
    +
  • } /> ) diff --git a/apps/webapp/components/shared/media-sections.tsx b/apps/webapp/components/shared/media-sections.tsx index a70c42403..721ad2c59 100644 --- a/apps/webapp/components/shared/media-sections.tsx +++ b/apps/webapp/components/shared/media-sections.tsx @@ -13,7 +13,7 @@ export function MediaSections({ sections, lang }: MediaSectionsProps) { section?.videos?.length > 0 && (
    - + / {section.title}
    -
      +
        {section?.videos?.map((video, index) => ( = 2 && 'hidden sm:block', - index >= 4 && 'md:block', - index >= 5 && 'xl:block', + index === 3 ? 'md:hidden xl:block' : '', + index >= 4 && 'md:block lg:hidden 2xl:block', )} /> ))} diff --git a/apps/webapp/components/shared/section.tsx b/apps/webapp/components/shared/section.tsx index e562e17d1..14e6da22f 100644 --- a/apps/webapp/components/shared/section.tsx +++ b/apps/webapp/components/shared/section.tsx @@ -4,10 +4,10 @@ import Balancer from 'react-wrap-balancer' export function Section({ heading, children }: SectionProps) { return (
        -

        +

        {heading}

        -
        +
        {children}
        ) diff --git a/apps/webapp/components/ui/dropdown-menu.tsx b/apps/webapp/components/ui/dropdown-menu.tsx index ab65fc3cf..bc1f888fe 100644 --- a/apps/webapp/components/ui/dropdown-menu.tsx +++ b/apps/webapp/components/ui/dropdown-menu.tsx @@ -171,7 +171,7 @@ const DropdownMenuShortcut = ({ return ( , - ) => - identify({ - address, - properties, - }) + ) => identify(address, properties) const trackInteraction = ( label: string, diff --git a/apps/webapp/hooks/use-place-order.ts b/apps/webapp/hooks/use-place-order.ts index 6b5202d37..8beea7486 100644 --- a/apps/webapp/hooks/use-place-order.ts +++ b/apps/webapp/hooks/use-place-order.ts @@ -1,4 +1,4 @@ -import { TestnetEasyAuction } from 'app-contracts' +import { TestnetEasyAuction } from '@repo/contracts' import { Address } from 'viem' import { useWriteContract } from 'wagmi' diff --git a/apps/webapp/hooks/use-session.tsx b/apps/webapp/hooks/use-session.tsx index ed603ed21..f6224d6d8 100644 --- a/apps/webapp/hooks/use-session.tsx +++ b/apps/webapp/hooks/use-session.tsx @@ -6,11 +6,9 @@ import { useSupabaseClient } from '@/services/supabase' import { createContextHook } from '@blockmatic/hooks-utils' import { useConnectModal } from '@rainbow-me/rainbowkit' import type { Tables } from '@repo/supabase' -import { uniq } from 'lodash' import { usePathname, useRouter, useSearchParams } from 'next/navigation' import React, { type ReactNode, useEffect, useState } from 'react' import { isMobile } from 'react-device-detect' -import toast from 'react-hot-toast' import { useAsync, useLocalStorage, useToggle } from 'react-use' import { v4 as uuidv4 } from 'uuid' import { type Config, type UseAccountReturnType, useAccount } from 'wagmi' @@ -53,41 +51,25 @@ function useSessionFn() { console.log('🔐 verifyAccount') let user - const updatedAddresses = [] try { user = await supabase - .from('user') - .select('id, address') + .from('account') + .select('account') .eq('account', session.account) .single() - updatedAddresses.push( - ...(user?.data?.address.length - ? [...user?.data?.address, account.address as string] - : [account.address as string]), - ) } catch (error) { console.error('🔐 verifyAccount error', error) } - await supabase.from('user').upsert( + await supabase.from('account').upsert( { - id: user?.data?.id, account: session.account, - address: uniq(updatedAddresses.filter(Boolean)), }, { onConflict: 'account', }, ) - - // * If the address is new, we show the toaster. - if ( - account.address && - !user?.data?.address.includes(account.address as string) - ) { - toast('Address linked successfully!', { icon: '🔗' }) - } } useEffect(() => { diff --git a/apps/webapp/lib/config.ts b/apps/webapp/lib/config.ts index 4b1783b74..34fa1fe55 100644 --- a/apps/webapp/lib/config.ts +++ b/apps/webapp/lib/config.ts @@ -6,7 +6,7 @@ export const appConfig = { env, eosRpc: 'https://eos.greymass.com', multibase: { - key: process.env.MULTIBASE_API_KEY || '', + key: process.env.NEXT_PUBLIC_MULTIBASE_API_KEY || '', }, features: { enableWalletAccess: diff --git a/apps/webapp/lib/projects.ts b/apps/webapp/lib/projects.ts index 88a56e206..dec4e1a0d 100644 --- a/apps/webapp/lib/projects.ts +++ b/apps/webapp/lib/projects.ts @@ -1,4 +1,4 @@ -import { TestnetMBOTSPL, type TokenContractData } from 'app-contracts' +import { TestnetMBOTSPL, type TokenContractData } from '@repo/contracts' import { merge } from 'lodash' import type { StaticImageData } from 'next/image' import BitcashPic from '../assets/img/bitcash.webp' @@ -27,6 +27,7 @@ export const projects: Project[] = [ twitterUsername: 'bitcashorg', telegramGroup: 'bitlauncher', discordServer: 'KuR48XUxnG', + presaleId: 1, // in database }, { id: 2, @@ -94,6 +95,7 @@ export interface Project { presaleOpen?: boolean registrationOpen?: boolean auctionClosed?: boolean + presaleId?: number } export interface ContentSection { diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 66c8dd0d1..3b096a2ad 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "launchpad-webapp", - "version": "1.0.0", + "version": "1.1.0", "scripts": { "dev": "rm -rf .next && next dev --turbo", "build": "next build", @@ -14,6 +14,7 @@ "type-check:watch": "tsc --noEmit -w", "translate": "bun run scripts/translate.ts", "translate:category": "bun run scripts/translate-category.ts", + "gen-cms": "bun run scripts/gen-cms.ts", "vercel:install": "npm i -g node-gyp; bun install" }, "dependencies": { @@ -24,7 +25,7 @@ "@genql/runtime": "^2.10.0", "@headlessui/react": "^2.1.2", "@hookform/resolvers": "^3.9.0", - "@multibase/js": "^0.1.15", + "@multibase/js": "latest", "@next/bundle-analyzer": "^14.2.5", "@next/env": "14.2.5", "@next/third-parties": "latest", @@ -40,9 +41,11 @@ "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", "@rainbow-me/rainbowkit": "2.x", + "@repo/contracts": "workspace:*", + "@repo/tokens": "workspace:*", + "@repo/utils": "workspace:*", "@supabase/ssr": "^0.4.0", "@supabase/supabase-js": "^2.44.4", - "@tailwindcss/typography": "0.5.13", "@tanstack/react-query": "^5.51.11", "@vercel/analytics": "^1.3.1", "@vercel/og": "^0.6.2", @@ -50,6 +53,7 @@ "@wagmi/connectors": "5.0.26", "@wharfkit/antelope": "^1.0.8", "ai": "^3.2.35", + "app-env": "workspace:*", "axios": "^1.7.2", "camelize-ts": "^3.0.0", "class-variance-authority": "^0.7.0", @@ -67,6 +71,7 @@ "next": "14.2.5", "next-share": "^0.27.0", "next-themes": "^0.3.0", + "nookies": "^2.5.2", "pako": "^2.1.0", "pino-pretty": "^11.2.1", "react": "18.x", @@ -84,18 +89,15 @@ "resend": "^3.5.0", "server-only": "^0.0.1", "sharp": "^0.33.4", - "app-contracts": "workspace:*", - "app-env": "workspace:*", - "app-lib": "workspace:*", "uuid": "^10.0.0", "vconsole": "^3.15.1", "vercel": "^35.1.0", "viem": "latest", "wagmi": "2.9.3", - "zod": "^3.23.8", - "nookies": "^2.5.2" + "zod": "^3.23.8" }, "devDependencies": { + "@genql/cli": "^6.3.3", "@tailwindcss/typography": "^0.5.13", "@types/lodash": "^4.17.7", "@types/negotiator": "^0.6.3", diff --git a/apps/webapp/scripts/cms-ql.sh b/apps/webapp/scripts/cms-ql.sh new file mode 100755 index 000000000..f1129deeb --- /dev/null +++ b/apps/webapp/scripts/cms-ql.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# cms-ql.sh: Utility script for generating GraphQL types +# Usage: ./cms-ql.sh +# Dependencies: GraphQL schema must be properly configured +# eval $(grep '^NEXT_PUBLIC_CMS_GRAPHQL_API' ./.env) && genql --endpoint $NEXT_PUBLIC_CMS_GRAPHQL_API --output ./graphql/generated/cms -H 'content-type: application/json' +# eval $(grep '^NEXT_PUBLIC_CMS_GRAPHQL_API' ./.env) && eval $(grep '^NEXT_PUBLIC_CMS_API_KEY' ./.env) && genql --endpoint $NEXT_PUBLIC_GRAPHQL_API --output ./graphql/generated/cms -H 'content-type: application/json' -H 'Authorization: Bearer $NEXT_PUBLIC_CMS_API_KEY' +eval $(grep '^NEXT_PUBLIC_CMS_GRAPHQL_API' ./.env) && \ +eval $(grep '^NEXT_PUBLIC_CMS_API_KEY' ./.env) && \ +npx --verbose genql --endpoint $NEXT_PUBLIC_CMS_GRAPHQL_API \ +--output ./services/datocms/graphql/generated/cms \ +-H 'content-type: application/json' \ +-H "'Authorization: Bearer $NEXT_PUBLIC_CMS_API_KEY'" \ No newline at end of file diff --git a/apps/webapp/scripts/translate.ts b/apps/webapp/scripts/translate.ts index 932c30317..eb24fa969 100644 --- a/apps/webapp/scripts/translate.ts +++ b/apps/webapp/scripts/translate.ts @@ -1,4 +1,3 @@ -import * as path from 'path' import { type Lang, locales } from '@/dictionaries/locales' import { promiseAllWithConcurrencyLimit } from '@/lib/utils' import { anthropicTranslate } from '@/services/anthropic' @@ -9,9 +8,10 @@ import { extractTitleAndDescriptionNested, injectTextAfterTranslation, } from '@/services/datocms/translation/utils' -import { getErrorMessage } from 'app-lib' +import { getErrorMessage } from '@repo/utils' import * as fs from 'fs/promises' import _ from 'lodash' +import * as path from 'path' async function processFile( file: string, diff --git a/apps/webapp/services/datocms/datacms-blog-category.service.ts b/apps/webapp/services/datocms/datacms-blog-category.service.ts index fb6cbd927..9d943ee09 100644 --- a/apps/webapp/services/datocms/datacms-blog-category.service.ts +++ b/apps/webapp/services/datocms/datacms-blog-category.service.ts @@ -6,6 +6,7 @@ import type { BlogBitcashRecord, BlogBitcoinModelFilter, BlogBitcoinRecord, + BlogBitlauncherRecord, BlogCryptoModelFilter, BlogInvestingRecord, BlogNewsModelFilter, @@ -55,6 +56,9 @@ export async function getBlogCategory( case 'bitcash': categoryRecordName = 'allBlogBitcashes' break + case 'bitlauncher': + categoryRecordName = 'allBlogBitlaunchers' + break case 'ai-research': categoryRecordName = 'allResearchAis' break @@ -121,10 +125,10 @@ export async function getBlogCategory( throw new Error('No records has been found for ' + category) } } catch (err) { - // console.log( - // 'datocms-blog-category.service::getBlogCategory::[ERROR]:: ' + category, - // err - // ) + console.log( + 'datocms-blog-category.service::getBlogCategory::[ERROR]:: ' + category, + err, + ) error = (err as Error).message } finally { @@ -419,6 +423,53 @@ export async function getBlogCategories(): Promise { }, }, + allBlogBitlaunchers: { + __args: { + orderBy: ['_publishedAt_DESC'], + locale: 'en', + fallbackLocales: ['en'], + }, + id: true, + topics: true, + title: true, + slug: true, + authorName: true, + authorPicture: { + url: true, + }, + _publishedAt: true, + description: true, + thumbnail: { + url: true, + }, + contentBlock: { + mainContent: { + value: true, + }, + topImages: { + basename: true, + height: true, + width: true, + filename: true, + format: true, + alt: true, + url: true, + }, + }, + seo: { + description: true, + title: true, + twitterCard: true, + image: { + width: true, + height: true, + title: true, + alt: true, + url: true, + }, + }, + }, + allBlogBitcashes: { __args: { orderBy: ['_publishedAt_DESC'], @@ -525,6 +576,7 @@ export async function getBlogCategories(): Promise { allBlogStartups: [defaultBlogArticle], allBlogAis: [defaultBlogArticle], allBlogNews: [defaultBlogArticle], + allBlogBitlaunchers: [defaultBlogArticle], allBlogBitcashes: [defaultBlogArticle], allResearchAis: [defaultBlogArticle], } @@ -609,6 +661,7 @@ export interface BlogArticleRecord { id: | BlogAiRecord['id'] | BlogNewsRecord['id'] + | BlogBitlauncherRecord['id'] | BlogBitcashRecord['id'] | BlogBitcoinRecord['id'] | BlogStartupRecord['id'] @@ -637,6 +690,7 @@ export interface getBlogCategoriesTypes { allBlogStartups: BlogArticleRecord[] allBlogAis: BlogArticleRecord[] allBlogNews: BlogArticleRecord[] + allBlogBitlaunchers: BlogArticleRecord[] allBlogBitcashes: BlogArticleRecord[] allResearchAis: BlogArticleRecord[] } diff --git a/apps/webapp/services/datocms/datocms-all-articles.service.ts b/apps/webapp/services/datocms/datocms-all-articles.service.ts index 0e2ddb6d3..204264a03 100644 --- a/apps/webapp/services/datocms/datocms-all-articles.service.ts +++ b/apps/webapp/services/datocms/datocms-all-articles.service.ts @@ -85,6 +85,7 @@ const categoryMap: Record = { ai: 'allBlogAis', news: 'allBlogNews', bitcash: 'allBlogBitcashes', + bitlauncher: 'allBlogBitlaunchers', 'ai-research': 'allResearchAis', } diff --git a/apps/webapp/services/datocms/datocms-blog.service.ts b/apps/webapp/services/datocms/datocms-blog.service.ts index e2d753cb2..a15a389cd 100644 --- a/apps/webapp/services/datocms/datocms-blog.service.ts +++ b/apps/webapp/services/datocms/datocms-blog.service.ts @@ -1,9 +1,9 @@ -import * as fs from 'fs' -import path from 'path' import type { Lang } from '@/dictionaries/locales' import { getFilePath, parseFile } from '@/lib/file' -import { getErrorMessage } from 'app-lib' +import { getErrorMessage } from '@repo/utils' +import * as fs from 'fs' import { uniq } from 'lodash' +import path from 'path' import { type BlogArticleRecord, getBlogCategory, @@ -21,9 +21,10 @@ export const getBlogData = async () => { { investingData, investingError }, { startupData, startupError }, { aiData, aiError }, - { newsData, newsError }, + // { newsData, newsError }, { bitcashData, bitcashError }, { aiResearchData, researchError }, + { bitlauncherData, bitlauncherError }, ] = await Promise.all([ getLayoutText(), getPageSeoText('home'), @@ -32,9 +33,10 @@ export const getBlogData = async () => { getBlogCategory('investing', undefined, 5), getBlogCategory('startup', undefined, 5), getBlogCategory('ai', undefined, 5), - getBlogCategory('news', undefined, 5), + // getBlogCategory('news', undefined, 5), getBlogCategory('bitcash', undefined, 5), getBlogCategory('ai-research', undefined, 5), + getBlogCategory('bitlauncher', undefined, 5), ]) return { i18n, @@ -49,12 +51,14 @@ export const getBlogData = async () => { startupError, aiData, aiError, - newsData, - newsError, + // newsData, + // newsError, bitcashData, bitcashError, aiResearchData, researchError, + bitlauncherError, + bitlauncherData, } } @@ -64,33 +68,41 @@ export async function getArticleSections( const dirPath = `/dictionaries/${lang}/blog/` const fileName = `blog-index.json` const filePath = path.resolve(dirPath, fileName) - // return cached translations + let fileContents: { sections: ArticlesSection[] } | undefined + // return cached translations try { - const fileContents = parseFile(filePath) - return fileContents.sections + // ? The idea is to get the file contents and return it if it exists and it should be up to date with the latest on DatoCMS, so we can reduce the amount of requests to DatoCMS + fileContents = parseFile(filePath) + // ? Due we are not updating the file contents frequently, we can return the file contents directly + // console.info('in', process.env.NODE_ENV) + if (process.env.NODE_ENV === 'production') { + return fileContents?.sections as ArticlesSection[] + } } catch (error) { - // console.log('😬 translation not found', getErrorMessage(error)) + console.log('😬 translation not found', getErrorMessage(error)) try { + console.log('😬 trying english version', { dirPath, filePath, fileName }) const englishVersion = parseFile(`/dictionaries/en/blog/${fileName}`) if (englishVersion) { - // console.log('😬 returning english version') + console.log('😬 returning english version') return englishVersion.sections } } catch (error) { - console.log('❌ error', error) - return [] + console.error('❌ Failed to get cached file. Fetching new data', error) } } + const { bitcoinData, cryptoData, investingData, startupData, aiData, - newsData, + // newsData, bitcashData, aiResearchData, + bitlauncherData, } = await getBlogData() const sections: ArticlesSection[] = [ @@ -104,10 +116,15 @@ export async function getArticleSections( slug: 'ai-research', articles: (aiResearchData?.slice(0, 4) || []) as BlogArticleRecord[], }, + // { + // name: 'News', + // slug: 'news', + // articles: (newsData?.slice(0, 4) || []) as BlogArticleRecord[], + // }, { - name: 'News', - slug: 'news', - articles: (newsData?.slice(0, 4) || []) as BlogArticleRecord[], + name: 'Bitlauncher', + slug: 'bitlauncher', + articles: (bitlauncherData?.slice(0, 4) || []) as BlogArticleRecord[], }, { name: 'Bitcash', @@ -142,10 +159,37 @@ export async function getArticleSections( }) }) - fs.mkdirSync(dirPath, { recursive: true }) - fs.writeFileSync(filePath, JSON.stringify({ sections }, null, 2)) + // Check file sections against new sections. If no section found on files, then we update the sections + const fileSections = fileContents?.sections || [] + const updatedSections = sections.map((section) => { + const fileSection = fileSections?.find( + (fs) => + fs.name === section.name && + fs.articles[0]._publishedAt === section.articles[0]._publishedAt, + ) + if (fileSection) { + return fileSection + } + return section + }) + fileContents = { + sections: updatedSections, + } - return sections + // TODO: Fix cache file update on production build. + // ! It's not updating the file and we might choose to add cache to the user's browser instead. + // ? Or moving this to actions.ts + try { + fs.mkdirSync(getFilePath(dirPath), { recursive: true }) + fs.writeFileSync( + getFilePath(filePath), + JSON.stringify(fileContents, null, 2), + ) + } catch (error) { + console.error('❌❌❌❌ Failed to update cache on file.', error) + } + + return sections as ArticlesSection[] } export async function getRecentArticleSections(): Promise { @@ -155,7 +199,7 @@ export async function getRecentArticleSections(): Promise { investingData, startupData, aiData, - newsData, + // newsData, bitcashData, aiResearchData, } = await getBlogData() @@ -199,19 +243,26 @@ export async function getBlogCategoryLandingData(lang: Lang, category: string) { const filePath = path.resolve(dirPath, fileName) // console.log('getBlogCategoryLandingData', { dirPath, filePath }) + let fileContents: { sections: ArticlesSection[] } | undefined // return cached translations try { - const fileContents = parseFile(filePath) - return fileContents + // ? The idea is to get the file contents and return it if it exists and it should be up to date with the latest on DatoCMS, so we can reduce the amount of requests to DatoCMS + fileContents = parseFile(filePath) + // ? Due we are not updating the file contents frequently, we can return the file contents directly + // console.info('in', process.env.NODE_ENV) + if (process.env.NODE_ENV === 'production') { + return fileContents?.sections as ArticlesSection[] + } } catch (error) { // console.log('error', error) try { const englishVersion = parseFile( `/dictionaries/en/blog/${category}/${fileName}`, ) - if (englishVersion) return englishVersion - } catch (error) {} + } catch (error) { + console.error('❌ Failed to get cached file. Fetching new data', error) + } } // replacing category kebab case with camel case @@ -250,10 +301,37 @@ export async function getBlogCategoryLandingData(lang: Lang, category: string) { } }) - const result = { sections, pageSeo } + // Check file sections against new sections. If no section found on files, then we update the sections + const fileSections = fileContents?.sections || [] + const updatedSections = sections.map((section) => { + const fileSection = fileSections?.find( + (fs) => + fs.name === section.name && + fs.articles[0]._publishedAt === section.articles[0]._publishedAt, + ) + if (fileSection) { + return fileSection + } + return section + }) + fileContents = { + sections: updatedSections, + } + + const result = { + sections: fileContents?.sections as ArticlesSection[], + pageSeo, + } - fs.mkdirSync(getFilePath(dirPath), { recursive: true }) - fs.writeFileSync(getFilePath(filePath), JSON.stringify(result, null, 2)) + // TODO: Fix cache file update on production build. + // ! It's not updating the file and we might choose to add cache to the user's browser instead. + // ? Or moving this to actions.ts + try { + fs.mkdirSync(getFilePath(dirPath), { recursive: true }) + fs.writeFileSync(getFilePath(filePath), JSON.stringify(result, null, 2)) + } catch (error) { + console.error('❌❌❌❌ Failed to update cache on file.', error) + } return result } @@ -269,19 +347,29 @@ export async function getBlogArticleData( category: string, slug: string, ) { - const dirPath = `dictionaries/${lang}/blog/${category}` + const dirPath = `/dictionaries/${lang}/blog/${category}` const fileName = `${slug}.json` const filePath = path.resolve(dirPath, fileName) + let fileContents: BlogArticleData | undefined // return cached translations try { - const fileContents: BlogArticleData = parseFile(filePath) - return fileContents + // ? The idea is to get the file contents and return it if it exists and it should be up to date with the latest on DatoCMS, so we can reduce the amount of requests to DatoCMS + fileContents = parseFile(filePath) + // ? Due we are not updating the file contents frequently, we can return the file contents directly + // console.info('in', process.env.NODE_ENV) + if (process.env.NODE_ENV === 'production') { + return fileContents as BlogArticleData + } } catch (error) { - const englishVersion: BlogArticleData = parseFile( - `/dictionaries/en/blog/${category}/${slug}.json`, - ) - if (englishVersion) return englishVersion + try { + const englishVersion: BlogArticleData = parseFile( + `/dictionaries/en/blog/${category}/${slug}.json`, + ) + if (englishVersion) return englishVersion + } catch (error) { + console.error('❌ Failed to get cached file. Fetching new data', error) + } } // console.log('getBlogArticleData', { locale, category, slug }) @@ -342,8 +430,27 @@ export async function getBlogArticleData( // always create an english dictionary const result: BlogArticleData = { relatedBlogs, blogContent, topics } const fullPath = getFilePath(filePath) - fs.mkdirSync(path.dirname(fullPath), { recursive: true }) - fs.writeFileSync(fullPath, JSON.stringify(result, null, 2)) + + if (fileContents?.blogContent) { + // Check file article against new article. If no updated found on files, then we update the article + const fileArticle = fileContents + if ( + fileArticle.blogContent.title === blogContent.title && + fileArticle.blogContent._publishedAt === blogContent._publishedAt + ) { + return fileArticle + } + } + + // TODO: Fix cache file update on production build. + // ! It's not updating the file and we might choose to add cache to the user's browser instead. + // ? Or moving this to actions.ts + try { + fs.mkdirSync(getFilePath(dirPath), { recursive: true }) + fs.writeFileSync(getFilePath(filePath), JSON.stringify(result, null, 2)) + } catch (error) { + console.error('❌❌❌❌ Failed to update cache on file.', error) + } return result } diff --git a/apps/webapp/services/datocms/graphql/generated/cms/runtime/batcher.ts b/apps/webapp/services/datocms/graphql/generated/cms/runtime/batcher.ts index fe256ec92..622d6018e 100644 --- a/apps/webapp/services/datocms/graphql/generated/cms/runtime/batcher.ts +++ b/apps/webapp/services/datocms/graphql/generated/cms/runtime/batcher.ts @@ -1,5 +1,5 @@ -// @ts-nocheck import { GenqlError } from './error' +// @ts-nocheck import type { GraphqlOperation } from './generateGraphqlOperation' type Variables = Record @@ -46,28 +46,41 @@ function dispatchQueueBatch(client: QueryBatcher, queue: Queue): void { if (batchedQuery.length === 1) { batchedQuery = batchedQuery[0] } + ;(() => { + try { + return client.fetcher(batchedQuery) + } catch (e) { + return Promise.reject(e) + } + })() + .then((responses: any) => { + if (queue.length === 1 && !Array.isArray(responses)) { + if (responses.errors && responses.errors.length) { + queue[0].reject(new GenqlError(responses.errors, responses.data)) + return + } - client.fetcher(batchedQuery).then((responses: any) => { - if (queue.length === 1 && !Array.isArray(responses)) { - if (responses.errors && responses.errors.length) { - queue[0].reject(new GenqlError(responses.errors, responses.data)) + queue[0].resolve(responses) return + } else if (responses.length !== queue.length) { + throw new Error('response length did not match query length') } - queue[0].resolve(responses) - return - } else if (responses.length !== queue.length) { - throw new Error('response length did not match query length') - } - - for (let i = 0; i < queue.length; i++) { - if (responses[i].errors && responses[i].errors.length) { - queue[i].reject(new GenqlError(responses[i].errors, responses[i].data)) - } else { - queue[i].resolve(responses[i]) + for (let i = 0; i < queue.length; i++) { + if (responses[i].errors && responses[i].errors.length) { + queue[i].reject( + new GenqlError(responses[i].errors, responses[i].data), + ) + } else { + queue[i].resolve(responses[i]) + } } - } - }) + }) + .catch((e) => { + for (let i = 0; i < queue.length; i++) { + queue[i].reject(e) + } + }) } /** diff --git a/apps/webapp/services/datocms/graphql/generated/cms/runtime/fetcher.ts b/apps/webapp/services/datocms/graphql/generated/cms/runtime/fetcher.ts index d48b03f3e..d8cdd93cc 100644 --- a/apps/webapp/services/datocms/graphql/generated/cms/runtime/fetcher.ts +++ b/apps/webapp/services/datocms/graphql/generated/cms/runtime/fetcher.ts @@ -1,5 +1,6 @@ // @ts-nocheck import { QueryBatcher } from './batcher' + import type { ClientOptions } from './createClient' import { GenqlError } from './error' import type { GraphqlOperation } from './generateGraphqlOperation' diff --git a/apps/webapp/services/datocms/graphql/generated/cms/schema.graphql b/apps/webapp/services/datocms/graphql/generated/cms/schema.graphql index 28cafe4d9..8bca95d11 100644 --- a/apps/webapp/services/datocms/graphql/generated/cms/schema.graphql +++ b/apps/webapp/services/datocms/graphql/generated/cms/schema.graphql @@ -359,6 +359,129 @@ type BlogBitcoinRecord implements RecordInterface { topics: JsonField } +input BlogBitlauncherModelFilter { + _createdAt: CreatedAtFilter + id: ItemIdFilter + _firstPublishedAt: PublishedAtFilter + _publicationScheduledAt: PublishedAtFilter + _unpublishingScheduledAt: PublishedAtFilter + _publishedAt: PublishedAtFilter + _status: StatusFilter + _updatedAt: UpdatedAtFilter + _isValid: BooleanFilter + authorName: StringFilter + authorPicture: FileFilter + description: StringFilter + seo: SeoFilter + slug: SlugFilter + thumbnail: FileFilter + title: StringFilter + topics: JsonFilter + OR: [BlogBitlauncherModelFilter] + AND: [BlogBitlauncherModelFilter] +} + +enum BlogBitlauncherModelOrderBy { + _createdAt_ASC + _createdAt_DESC + id_ASC + id_DESC + _firstPublishedAt_ASC + _firstPublishedAt_DESC + _publicationScheduledAt_ASC + _publicationScheduledAt_DESC + _unpublishingScheduledAt_ASC + _unpublishingScheduledAt_DESC + _publishedAt_ASC + _publishedAt_DESC + _status_ASC + _status_DESC + _updatedAt_ASC + _updatedAt_DESC + _isValid_ASC + _isValid_DESC + authorName_ASC + authorName_DESC + description_ASC + description_DESC + title_ASC + title_DESC +} + +"""Record of type Bitlauncher Blog (blog_bitlauncher)""" +type BlogBitlauncherRecord implements RecordInterface { + _allDescriptionLocales( + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + ): [StringMultiLocaleField!] + _allSeoLocales( + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + ): [SeoFieldMultiLocaleField!] + _allTitleLocales( + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + ): [StringMultiLocaleField!] + _createdAt: DateTime! + + """Editing URL""" + _editingUrl: String + _firstPublishedAt: DateTime + _isValid: BooleanType! + _modelApiKey: String! + _publicationScheduledAt: DateTime + _publishedAt: DateTime + + """Generates SEO and Social card meta tags to be used in your frontend""" + _seoMetaTags( + """The locale to use to fetch the field's content""" + locale: SiteLocale + ): [Tag!]! + _status: ItemStatus! + _unpublishingScheduledAt: DateTime + _updatedAt: DateTime! + authorName: String + authorPicture: FileField + contentBlock: [ContentBlockRecord!]! + description( + """The locale to use to fetch the field's content""" + locale: SiteLocale + + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + ): String + id: ItemId! + seo( + """The locale to use to fetch the field's content""" + locale: SiteLocale + + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + ): SeoField + slug: String + thumbnail: FileField + title( + """The locale to use to fetch the field's content""" + locale: SiteLocale + + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + ): String + topics: JsonField +} + input BlogCryptoModelFilter { _createdAt: CreatedAtFilter id: ItemIdFilter @@ -1218,15 +1341,51 @@ input ImgixParams { """ auto: [ImgixParamsAuto!] + """ + Background Removal Fallback + + Overrides default fallback behavior for bg-remove failures. + + [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-remove) + """ + bgRemoveFallback: BooleanType + """ Background Removal Removes background from image. - [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background-removal/bg-remove) + [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-remove) """ bgRemove: BooleanType + """ + Background Removal Fallback + + Overrides default fallback behavior for bg-replace failures. + + [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-replace) + """ + bgReplaceFallback: BooleanType + + """ + Background Replacement Negative Prompt + + Provides a negative text suggestion for background replacement. + + [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-replace-neg-prompt) + """ + bgReplaceNegPrompt: String + + """ + Background Replacement + + Replaces background from image using a string based prompt. + + [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-replace) + """ + bgReplace: String + """ Background Color @@ -1655,6 +1814,149 @@ input ImgixParams { """ fillColor: String + """ + Fill Generative Fallback + + Sets the fallback behavior for generative fill. + + Depends on: `fit=fill`, `fill=gen` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-fallback) + """ + fillGenFallback: BooleanType + + """ + Fill Generative Negative Prompt + + Provides a negative text suggestion to the generative fill parameter. Used to reduce the probability of a subject, detail, or object appearing in generative output. + + Depends on: `fit=fill`, `fill=gen` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-neg-prompt) + """ + fillGenNegPrompt: String + + """ + Fill Generative Position + + Sets the position of the Origin Image in relation to the generative fill. + + Depends on: `fit=fill`, `fill=gen` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-pos) + """ + fillGenPos: [ImgixParamsFillGenPos!] + + """ + Fill Generative Prompt + + Provides a text suggestion to the generative fill parameter. + + Depends on: `fit=fill`, `fill=gen` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-prompt) + """ + fillGenPrompt: String + + """ + Fill Generative Seed + + Sets the generative seed value. Used to generate similar outputs from different prompts. + + Depends on: `fit=fill`, `fill=gen` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-seed) + """ + fillGenSeed: IntType + + """ + Fill Gradient Color Space + + Defines the color space as linear, sRGB, Oklab, HSL, or LCH for gradient color interpolation + + Depends on: `fit=fill`, `fill=gradient` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-cs) + """ + fillGradientCs: ImgixParamsFillGradientCs + + """ + Fill Gradient Linear Direction + + The fill-gradient-linear-direction specifies the gradient's direction, flowing towards the bottom, top, right, or left + + Depends on: `fit=fill`, `fill=gen` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-linear-direction) + """ + fillGradientLinearDirection: [ImgixParamsFillGradientLinearDirection!] + + """ + Fill Gradient Linear + + Blends a gradient between two colors, {color1} and {color2}, along a straight path + + Depends on: `fit=fill`, `fill=gradient` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-linear) + """ + fillGradientLinear: String + + """ + Fill Gradient Radial Radius + + Parameter defines the radial gradient's radius as pixels or a percentage (0.0-1.0) of the image's smallest dimension + + Depends on: `fit=fill`, `fill=gradient` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-radial-radius) + """ + fillGradientRadialRadius: String + + """ + Fill Gradient Radial X + + Specifies the location of the radial gradient's center along the x-axis, using either a pixel value or a floating point percentage (ranging from 0.0 to 1.0) of the image's width + + Depends on: `fit=fill`, `fill=gradient` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-radial-x) + """ + fillGradientRadialX: FloatType + + """ + Fill Gradient Radial Y + + Parameter sets the radial gradient's center on the y-axis, using pixels or a 0.0 to 1.0 percentage of the image's height + + Depends on: `fit=fill`, `fill=gradient` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-radial-y) + """ + fillGradientRadialY: FloatType + + """ + Fill Gradient Radial + + The fill-gradient-radial parameter creates a circular gradient transitioning from a central color (Color1) to an outer color (Color2) + + Depends on: `fit=fill`, `fill=gradient` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-radial) + """ + fillGradientRadial: String + + """ + Fill Gradient Type + + Specifies if a gradient is radial (circular) or linear (straight) + + Depends on: `fit=fill`, `fill=gradient` + + [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-type) + """ + fillGradientType: ImgixParamsFillGradientType + """ Fill Mode @@ -1763,6 +2065,8 @@ input ImgixParams { """ Animated Gif Quality + Specifies the quality of the animated gif. The higher the value, the better more compression is applied. + Depends on: `fm=gif` """ gifQ: IntType @@ -1844,6 +2148,13 @@ input ImgixParams { """ iptc: ImgixParamsIptc + """ + Jpg Progressive + + Specifies whether or not a jpg/jpeg uses progressive (true) or baseline (false) + """ + jpgProgressive: BooleanType + """ Animation Loop Count @@ -2272,6 +2583,13 @@ input ImgixParams { """ skip: IntType + """ + Sanitize Svg + + Specifies whether to sanitize an SVG. + """ + svgSanitize: BooleanType + """ Transparency @@ -2411,17 +2729,6 @@ input ImgixParams { """ txtLead: IntType - """ - Text Ligatures - - Controls the level of ligature substitution - - Depends on: `txt` - - [Open Imgix reference »](https://docs.imgix.com/apis/url/text/txt-lig) - """ - txtLig: IntType - """ Text Outline Color @@ -2530,6 +2837,24 @@ input ImgixParams { """ txt: String + """ + Super Resolution Fallback + + Overrides default fallback behavior for super resolution failures + + [Open Imgix reference »](https://docs.imgix.com/apis/rendering/super-resolution/upscale-fallback) + """ + upscaleFallback: BooleanType + + """ + Super Resolution + + Uses generative AI fill to upscale low resolution images. + + [Open Imgix reference »](https://docs.imgix.com/apis/rendering/super-resolution/upscale) + """ + upscale: BooleanType + """ Unsharp Mask @@ -2567,6 +2892,13 @@ input ImgixParams { [Open Imgix reference »](https://docs.imgix.com/apis/url/size/w) """ w: FloatType + + """ + Bypasses any [DatoCMS Automatic Image Optimization](https://www.datocms.com/docs/cdn-settings/advanced-asset-settings) that might be set up for the project. + + Exercise caution when using this parameter, as it could significantly increase your bandwidth costs. + """ + skipDefaultOptimizations: BooleanType } enum ImgixParamsAuto { @@ -2651,6 +2983,38 @@ enum ImgixParamsCs { enum ImgixParamsFill { solid blur + gen + generative + gradient +} + +enum ImgixParamsFillGenPos { + top + bottom + middle + left + right + center +} + +enum ImgixParamsFillGradientCs { + linear + srgb + oklab + hsl + lch +} + +enum ImgixParamsFillGradientLinearDirection { + top + bottom + left + right +} + +enum ImgixParamsFillGradientType { + linear + radial } enum ImgixParamsFit { @@ -3393,6 +3757,13 @@ type Query { filter: BlogBitcoinModelFilter ): CollectionMetadata! + """Returns meta information regarding a record collection""" + _allBlogBitlaunchersMeta( + """The locale to use to fetch the field's content""" + locale: SiteLocale + filter: BlogBitlauncherModelFilter + ): CollectionMetadata! + """Returns meta information regarding a record collection""" _allBlogCryptosMeta( """The locale to use to fetch the field's content""" @@ -3510,6 +3881,25 @@ type Query { orderBy: [BlogBitcoinModelOrderBy] = [_updatedAt_DESC] ): [BlogBitcoinRecord!]! + """Returns a collection of records""" + allBlogBitlaunchers( + """The locale to use to fetch the field's content""" + locale: SiteLocale + + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + + """Skip the first results""" + skip: IntType + + """Limit the number of results""" + first: IntType = 20 + filter: BlogBitlauncherModelFilter + orderBy: [BlogBitlauncherModelOrderBy] = [_updatedAt_DESC] + ): [BlogBitlauncherRecord!]! + """Returns a collection of records""" allBlogCryptos( """The locale to use to fetch the field's content""" @@ -3682,6 +4072,19 @@ type Query { orderBy: [BlogBitcoinModelOrderBy] = [_updatedAt_DESC] ): BlogBitcoinRecord + """Returns a specific record""" + blogBitlauncher( + """The locale to use to fetch the field's content""" + locale: SiteLocale + + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + filter: BlogBitlauncherModelFilter + orderBy: [BlogBitlauncherModelOrderBy] = [_updatedAt_DESC] + ): BlogBitlauncherRecord + """Returns a specific record""" blogCrypto( """The locale to use to fetch the field's content""" @@ -4022,6 +4425,9 @@ enum SiteLocale { zh id vi + ko + pt + fr } """Specifies how to filter Slug fields""" @@ -4612,8 +5018,34 @@ input UploadUpdatedAtFilter { } type UploadVideoField { + alt( + """The locale to use to fetch the field's content""" + locale: SiteLocale + + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + ): String + blurUpThumb( + """ + Controls the "punch" value (~contrast) of the blurhash decoding algorithm (defaults to 1.0) + """ + punch: Float! = 1 + + """Maximum image dimension (defaults to 24px)""" + size: Int! = 24 + + """Image quality (defaults to 70%)""" + quality: Int! = 70 + + """Imgix transformations to apply to the image""" + imgixParams: ImgixParams + ): String + blurhash: String duration: Int framerate: Int + height: IntType! mp4Url( """Pick highest resolution available up to the specified argument""" res: VideoMp4Res @@ -4624,12 +5056,23 @@ type UploadVideoField { muxAssetId: String! muxPlaybackId: String! streamingUrl: String! + thumbhash: String thumbnailUrl( """ The file extension of the requested image format. Either png, jpg or gif """ format: MuxThumbnailFormatType = jpg ): String! + title( + """The locale to use to fetch the field's content""" + locale: SiteLocale + + """ + If you want to fallback to a default translation when a translation has not been found + """ + fallbackLocales: [SiteLocale!] + ): String + width: IntType! } """Specifies how to filter by width""" diff --git a/apps/webapp/services/datocms/graphql/generated/cms/schema.ts b/apps/webapp/services/datocms/graphql/generated/cms/schema.ts index d45dd185d..c9a7ed4fd 100644 --- a/apps/webapp/services/datocms/graphql/generated/cms/schema.ts +++ b/apps/webapp/services/datocms/graphql/generated/cms/schema.ts @@ -190,6 +190,63 @@ export interface BlogBitcoinRecord { __typename: 'BlogBitcoinRecord' } +export type BlogBitlauncherModelOrderBy = + | '_createdAt_ASC' + | '_createdAt_DESC' + | 'id_ASC' + | 'id_DESC' + | '_firstPublishedAt_ASC' + | '_firstPublishedAt_DESC' + | '_publicationScheduledAt_ASC' + | '_publicationScheduledAt_DESC' + | '_unpublishingScheduledAt_ASC' + | '_unpublishingScheduledAt_DESC' + | '_publishedAt_ASC' + | '_publishedAt_DESC' + | '_status_ASC' + | '_status_DESC' + | '_updatedAt_ASC' + | '_updatedAt_DESC' + | '_isValid_ASC' + | '_isValid_DESC' + | 'authorName_ASC' + | 'authorName_DESC' + | 'description_ASC' + | 'description_DESC' + | 'title_ASC' + | 'title_DESC' + +/** Record of type Bitlauncher Blog (blog_bitlauncher) */ +export interface BlogBitlauncherRecord { + _allDescriptionLocales: StringMultiLocaleField[] | null + _allSeoLocales: SeoFieldMultiLocaleField[] | null + _allTitleLocales: StringMultiLocaleField[] | null + _createdAt: Scalars['DateTime'] + /** Editing URL */ + _editingUrl: Scalars['String'] | null + _firstPublishedAt: Scalars['DateTime'] | null + _isValid: Scalars['BooleanType'] + _modelApiKey: Scalars['String'] + _publicationScheduledAt: Scalars['DateTime'] | null + _publishedAt: Scalars['DateTime'] | null + /** Generates SEO and Social card meta tags to be used in your frontend */ + _seoMetaTags: Tag[] + _status: ItemStatus + _unpublishingScheduledAt: Scalars['DateTime'] | null + _updatedAt: Scalars['DateTime'] + authorName: Scalars['String'] | null + authorPicture: FileField | null + contentBlock: ContentBlockRecord[] + description: Scalars['String'] | null + id: Scalars['ItemId'] + seo: SeoField | null + slug: Scalars['String'] | null + thumbnail: FileField | null + title: Scalars['String'] | null + topics: Scalars['JsonField'] | null + __typename: 'BlogBitlauncherRecord' +} + export type BlogCryptoModelOrderBy = | '_createdAt_ASC' | '_createdAt_DESC' @@ -573,7 +630,35 @@ export type ImgixParamsCrop = export type ImgixParamsCs = 'srgb' | 'adobergb1998' | 'tinysrgb' | 'strip' -export type ImgixParamsFill = 'solid' | 'blur' +export type ImgixParamsFill = + | 'solid' + | 'blur' + | 'gen' + | 'generative' + | 'gradient' + +export type ImgixParamsFillGenPos = + | 'top' + | 'bottom' + | 'middle' + | 'left' + | 'right' + | 'center' + +export type ImgixParamsFillGradientCs = + | 'linear' + | 'srgb' + | 'oklab' + | 'hsl' + | 'lch' + +export type ImgixParamsFillGradientLinearDirection = + | 'top' + | 'bottom' + | 'left' + | 'right' + +export type ImgixParamsFillGradientType = 'linear' | 'radial' export type ImgixParamsFit = | 'clamp' @@ -806,6 +891,8 @@ export interface Query { /** Returns meta information regarding a record collection */ _allBlogBitcoinsMeta: CollectionMetadata /** Returns meta information regarding a record collection */ + _allBlogBitlaunchersMeta: CollectionMetadata + /** Returns meta information regarding a record collection */ _allBlogCryptosMeta: CollectionMetadata /** Returns meta information regarding a record collection */ _allBlogInvestingsMeta: CollectionMetadata @@ -828,6 +915,8 @@ export interface Query { /** Returns a collection of records */ allBlogBitcoins: BlogBitcoinRecord[] /** Returns a collection of records */ + allBlogBitlaunchers: BlogBitlauncherRecord[] + /** Returns a collection of records */ allBlogCryptos: BlogCryptoRecord[] /** Returns a collection of records */ allBlogInvestings: BlogInvestingRecord[] @@ -848,6 +937,8 @@ export interface Query { /** Returns a specific record */ blogBitcoin: BlogBitcoinRecord | null /** Returns a specific record */ + blogBitlauncher: BlogBitlauncherRecord | null + /** Returns a specific record */ blogCrypto: BlogCryptoRecord | null /** Returns a specific record */ blogInvesting: BlogInvestingRecord | null @@ -874,6 +965,7 @@ export type RecordInterface = ( | BlogAiRecord | BlogBitcashRecord | BlogBitcoinRecord + | BlogBitlauncherRecord | BlogCryptoRecord | BlogInvestingRecord | BlogNewsRecord @@ -1089,13 +1181,20 @@ export type UploadType = | 'archive' export interface UploadVideoField { + alt: Scalars['String'] | null + blurUpThumb: Scalars['String'] | null + blurhash: Scalars['String'] | null duration: Scalars['Int'] | null framerate: Scalars['Int'] | null + height: Scalars['IntType'] mp4Url: Scalars['String'] | null muxAssetId: Scalars['String'] muxPlaybackId: Scalars['String'] streamingUrl: Scalars['String'] + thumbhash: Scalars['String'] | null thumbnailUrl: Scalars['String'] + title: Scalars['String'] | null + width: Scalars['IntType'] __typename: 'UploadVideoField' } @@ -1400,6 +1499,107 @@ export interface BlogBitcoinRecordGenqlSelection { __scalar?: boolean | number } +export interface BlogBitlauncherModelFilter { + _createdAt?: CreatedAtFilter | null + id?: ItemIdFilter | null + _firstPublishedAt?: PublishedAtFilter | null + _publicationScheduledAt?: PublishedAtFilter | null + _unpublishingScheduledAt?: PublishedAtFilter | null + _publishedAt?: PublishedAtFilter | null + _status?: StatusFilter | null + _updatedAt?: UpdatedAtFilter | null + _isValid?: BooleanFilter | null + authorName?: StringFilter | null + authorPicture?: FileFilter | null + description?: StringFilter | null + seo?: SeoFilter | null + slug?: SlugFilter | null + thumbnail?: FileFilter | null + title?: StringFilter | null + topics?: JsonFilter | null + OR?: (BlogBitlauncherModelFilter | null)[] | null + AND?: (BlogBitlauncherModelFilter | null)[] | null +} + +/** Record of type Bitlauncher Blog (blog_bitlauncher) */ +export interface BlogBitlauncherRecordGenqlSelection { + _allDescriptionLocales?: StringMultiLocaleFieldGenqlSelection & { + __args?: { + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + } + } + _allSeoLocales?: SeoFieldMultiLocaleFieldGenqlSelection & { + __args?: { + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + } + } + _allTitleLocales?: StringMultiLocaleFieldGenqlSelection & { + __args?: { + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + } + } + _createdAt?: boolean | number + /** Editing URL */ + _editingUrl?: boolean | number + _firstPublishedAt?: boolean | number + _isValid?: boolean | number + _modelApiKey?: boolean | number + _publicationScheduledAt?: boolean | number + _publishedAt?: boolean | number + /** Generates SEO and Social card meta tags to be used in your frontend */ + _seoMetaTags?: TagGenqlSelection & { + __args?: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + } + } + _status?: boolean | number + _unpublishingScheduledAt?: boolean | number + _updatedAt?: boolean | number + authorName?: boolean | number + authorPicture?: FileFieldGenqlSelection + contentBlock?: ContentBlockRecordGenqlSelection + description?: + | { + __args: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + } + } + | boolean + | number + id?: boolean | number + seo?: SeoFieldGenqlSelection & { + __args?: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + } + } + slug?: boolean | number + thumbnail?: FileFieldGenqlSelection + title?: + | { + __args: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + } + } + | boolean + | number + topics?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + export interface BlogCryptoModelFilter { _createdAt?: CreatedAtFilter | null id?: ItemIdFilter | null @@ -2126,14 +2326,46 @@ export interface ImgixParams { * [Open Imgix reference »](https://docs.imgix.com/apis/url/auto) */ auto?: ImgixParamsAuto[] | null + /** + * Background Removal Fallback + * + * Overrides default fallback behavior for bg-remove failures. + * + * [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-remove) + */ + bgRemoveFallback?: Scalars['BooleanType'] | null /** * Background Removal * * Removes background from image. * - * [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background-removal/bg-remove) + * [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-remove) */ bgRemove?: Scalars['BooleanType'] | null + /** + * Background Removal Fallback + * + * Overrides default fallback behavior for bg-replace failures. + * + * [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-replace) + */ + bgReplaceFallback?: Scalars['BooleanType'] | null + /** + * Background Replacement Negative Prompt + * + * Provides a negative text suggestion for background replacement. + * + * [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-replace-neg-prompt) + */ + bgReplaceNegPrompt?: Scalars['String'] | null + /** + * Background Replacement + * + * Replaces background from image using a string based prompt. + * + * [Open Imgix reference »](https://docs.imgix.com/apis/rendering/background/bg-replace) + */ + bgReplace?: Scalars['String'] | null /** * Background Color * @@ -2520,6 +2752,136 @@ export interface ImgixParams { * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-color) */ fillColor?: Scalars['String'] | null + /** + * Fill Generative Fallback + * + * Sets the fallback behavior for generative fill. + * + * Depends on: `fit=fill`, `fill=gen` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-fallback) + */ + fillGenFallback?: Scalars['BooleanType'] | null + /** + * Fill Generative Negative Prompt + * + * Provides a negative text suggestion to the generative fill parameter. Used to reduce the probability of a subject, detail, or object appearing in generative output. + * + * Depends on: `fit=fill`, `fill=gen` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-neg-prompt) + */ + fillGenNegPrompt?: Scalars['String'] | null + /** + * Fill Generative Position + * + * Sets the position of the Origin Image in relation to the generative fill. + * + * Depends on: `fit=fill`, `fill=gen` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-pos) + */ + fillGenPos?: ImgixParamsFillGenPos[] | null + /** + * Fill Generative Prompt + * + * Provides a text suggestion to the generative fill parameter. + * + * Depends on: `fit=fill`, `fill=gen` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-prompt) + */ + fillGenPrompt?: Scalars['String'] | null + /** + * Fill Generative Seed + * + * Sets the generative seed value. Used to generate similar outputs from different prompts. + * + * Depends on: `fit=fill`, `fill=gen` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gen-seed) + */ + fillGenSeed?: Scalars['IntType'] | null + /** + * Fill Gradient Color Space + * + * Defines the color space as linear, sRGB, Oklab, HSL, or LCH for gradient color interpolation + * + * Depends on: `fit=fill`, `fill=gradient` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-cs) + */ + fillGradientCs?: ImgixParamsFillGradientCs | null + /** + * Fill Gradient Linear Direction + * + * The fill-gradient-linear-direction specifies the gradient's direction, flowing towards the bottom, top, right, or left + * + * Depends on: `fit=fill`, `fill=gen` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-linear-direction) + */ + fillGradientLinearDirection?: ImgixParamsFillGradientLinearDirection[] | null + /** + * Fill Gradient Linear + * + * Blends a gradient between two colors, {color1} and {color2}, along a straight path + * + * Depends on: `fit=fill`, `fill=gradient` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-linear) + */ + fillGradientLinear?: Scalars['String'] | null + /** + * Fill Gradient Radial Radius + * + * Parameter defines the radial gradient's radius as pixels or a percentage (0.0-1.0) of the image's smallest dimension + * + * Depends on: `fit=fill`, `fill=gradient` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-radial-radius) + */ + fillGradientRadialRadius?: Scalars['String'] | null + /** + * Fill Gradient Radial X + * + * Specifies the location of the radial gradient's center along the x-axis, using either a pixel value or a floating point percentage (ranging from 0.0 to 1.0) of the image's width + * + * Depends on: `fit=fill`, `fill=gradient` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-radial-x) + */ + fillGradientRadialX?: Scalars['FloatType'] | null + /** + * Fill Gradient Radial Y + * + * Parameter sets the radial gradient's center on the y-axis, using pixels or a 0.0 to 1.0 percentage of the image's height + * + * Depends on: `fit=fill`, `fill=gradient` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-radial-y) + */ + fillGradientRadialY?: Scalars['FloatType'] | null + /** + * Fill Gradient Radial + * + * The fill-gradient-radial parameter creates a circular gradient transitioning from a central color (Color1) to an outer color (Color2) + * + * Depends on: `fit=fill`, `fill=gradient` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-radial) + */ + fillGradientRadial?: Scalars['String'] | null + /** + * Fill Gradient Type + * + * Specifies if a gradient is radial (circular) or linear (straight) + * + * Depends on: `fit=fill`, `fill=gradient` + * + * [Open Imgix reference »](https://docs.imgix.com/apis/url/fill/fill-gradient-type) + */ + fillGradientType?: ImgixParamsFillGradientType | null /** * Fill Mode * @@ -2617,6 +2979,8 @@ export interface ImgixParams { /** * Animated Gif Quality * + * Specifies the quality of the animated gif. The higher the value, the better more compression is applied. + * * Depends on: `fm=gif` */ gifQ?: Scalars['IntType'] | null @@ -2688,6 +3052,12 @@ export interface ImgixParams { * Determine if IPTC data should be passed for JPEG images. */ iptc?: ImgixParamsIptc | null + /** + * Jpg Progressive + * + * Specifies whether or not a jpg/jpeg uses progressive (true) or baseline (false) + */ + jpgProgressive?: Scalars['BooleanType'] | null /** * Animation Loop Count * @@ -3072,6 +3442,12 @@ export interface ImgixParams { * Skips every Nth frame starting with the first frame. */ skip?: Scalars['IntType'] | null + /** + * Sanitize Svg + * + * Specifies whether to sanitize an SVG. + */ + svgSanitize?: Scalars['BooleanType'] | null /** * Transparency * @@ -3198,16 +3574,6 @@ export interface ImgixParams { * [Open Imgix reference »](https://docs.imgix.com/apis/url/typesetting/txt-lead) */ txtLead?: Scalars['IntType'] | null - /** - * Text Ligatures - * - * Controls the level of ligature substitution - * - * Depends on: `txt` - * - * [Open Imgix reference »](https://docs.imgix.com/apis/url/text/txt-lig) - */ - txtLig?: Scalars['IntType'] | null /** * Text Outline Color * @@ -3306,6 +3672,22 @@ export interface ImgixParams { * [Open Imgix reference »](https://docs.imgix.com/apis/url/text/txt) */ txt?: Scalars['String'] | null + /** + * Super Resolution Fallback + * + * Overrides default fallback behavior for super resolution failures + * + * [Open Imgix reference »](https://docs.imgix.com/apis/rendering/super-resolution/upscale-fallback) + */ + upscaleFallback?: Scalars['BooleanType'] | null + /** + * Super Resolution + * + * Uses generative AI fill to upscale low resolution images. + * + * [Open Imgix reference »](https://docs.imgix.com/apis/rendering/super-resolution/upscale) + */ + upscale?: Scalars['BooleanType'] | null /** * Unsharp Mask * @@ -3340,6 +3722,12 @@ export interface ImgixParams { * [Open Imgix reference »](https://docs.imgix.com/apis/url/size/w) */ w?: Scalars['FloatType'] | null + /** + * Bypasses any [DatoCMS Automatic Image Optimization](https://www.datocms.com/docs/cdn-settings/advanced-asset-settings) that might be set up for the project. + * + * Exercise caution when using this parameter, as it could significantly increase your bandwidth costs. + */ + skipDefaultOptimizations?: Scalars['BooleanType'] | null } /** Specifies how to filter by usage */ @@ -3967,6 +4355,14 @@ export interface QueryGenqlSelection { } } /** Returns meta information regarding a record collection */ + _allBlogBitlaunchersMeta?: CollectionMetadataGenqlSelection & { + __args?: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + filter?: BlogBitlauncherModelFilter | null + } + } + /** Returns meta information regarding a record collection */ _allBlogCryptosMeta?: CollectionMetadataGenqlSelection & { __args?: { /** The locale to use to fetch the field's content */ @@ -4077,6 +4473,21 @@ export interface QueryGenqlSelection { } } /** Returns a collection of records */ + allBlogBitlaunchers?: BlogBitlauncherRecordGenqlSelection & { + __args?: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + /** Skip the first results */ + skip?: Scalars['IntType'] | null + /** Limit the number of results */ + first?: Scalars['IntType'] | null + filter?: BlogBitlauncherModelFilter | null + orderBy?: (BlogBitlauncherModelOrderBy | null)[] | null + } + } + /** Returns a collection of records */ allBlogCryptos?: BlogCryptoRecordGenqlSelection & { __args?: { /** The locale to use to fetch the field's content */ @@ -4215,6 +4626,17 @@ export interface QueryGenqlSelection { } } /** Returns a specific record */ + blogBitlauncher?: BlogBitlauncherRecordGenqlSelection & { + __args?: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + filter?: BlogBitlauncherModelFilter | null + orderBy?: (BlogBitlauncherModelOrderBy | null)[] | null + } + } + /** Returns a specific record */ blogCrypto?: BlogCryptoRecordGenqlSelection & { __args?: { /** The locale to use to fetch the field's content */ @@ -4345,6 +4767,7 @@ export interface RecordInterfaceGenqlSelection { on_BlogAiRecord?: BlogAiRecordGenqlSelection on_BlogBitcashRecord?: BlogBitcashRecordGenqlSelection on_BlogBitcoinRecord?: BlogBitcoinRecordGenqlSelection + on_BlogBitlauncherRecord?: BlogBitlauncherRecordGenqlSelection on_BlogCryptoRecord?: BlogCryptoRecordGenqlSelection on_BlogInvestingRecord?: BlogInvestingRecordGenqlSelection on_BlogNewsRecord?: BlogNewsRecordGenqlSelection @@ -4952,8 +5375,36 @@ export interface UploadUpdatedAtFilter { } export interface UploadVideoFieldGenqlSelection { + alt?: + | { + __args: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + } + } + | boolean + | number + blurUpThumb?: + | { + __args: { + /** Controls the "punch" value (~contrast) of the blurhash decoding algorithm (defaults to 1.0) */ + punch?: Scalars['Float'] + /** Maximum image dimension (defaults to 24px) */ + size?: Scalars['Int'] + /** Image quality (defaults to 70%) */ + quality?: Scalars['Int'] + /** Imgix transformations to apply to the image */ + imgixParams?: ImgixParams | null + } + } + | boolean + | number + blurhash?: boolean | number duration?: boolean | number framerate?: boolean | number + height?: boolean | number mp4Url?: | { __args: { @@ -4968,6 +5419,7 @@ export interface UploadVideoFieldGenqlSelection { muxAssetId?: boolean | number muxPlaybackId?: boolean | number streamingUrl?: boolean | number + thumbhash?: boolean | number thumbnailUrl?: | { __args: { @@ -4977,6 +5429,18 @@ export interface UploadVideoFieldGenqlSelection { } | boolean | number + title?: + | { + __args: { + /** The locale to use to fetch the field's content */ + locale?: SiteLocale | null + /** If you want to fallback to a default translation when a translation has not been found */ + fallbackLocales?: SiteLocale[] | null + } + } + | boolean + | number + width?: boolean | number __typename?: boolean | number __scalar?: boolean | number } @@ -5031,6 +5495,15 @@ export const isBlogBitcoinRecord = ( return BlogBitcoinRecord_possibleTypes.includes(obj.__typename) } +const BlogBitlauncherRecord_possibleTypes: string[] = ['BlogBitlauncherRecord'] +export const isBlogBitlauncherRecord = ( + obj?: { __typename?: any } | null, +): obj is BlogBitlauncherRecord => { + if (!obj?.__typename) + throw new Error('__typename is missing in "isBlogBitlauncherRecord"') + return BlogBitlauncherRecord_possibleTypes.includes(obj.__typename) +} + const BlogCryptoRecord_possibleTypes: string[] = ['BlogCryptoRecord'] export const isBlogCryptoRecord = ( obj?: { __typename?: any } | null, @@ -5213,6 +5686,7 @@ const RecordInterface_possibleTypes: string[] = [ 'BlogAiRecord', 'BlogBitcashRecord', 'BlogBitcoinRecord', + 'BlogBitlauncherRecord', 'BlogCryptoRecord', 'BlogInvestingRecord', 'BlogNewsRecord', @@ -5441,6 +5915,33 @@ export const enumBlogBitcoinModelOrderBy = { title_DESC: 'title_DESC' as const, } +export const enumBlogBitlauncherModelOrderBy = { + _createdAt_ASC: '_createdAt_ASC' as const, + _createdAt_DESC: '_createdAt_DESC' as const, + id_ASC: 'id_ASC' as const, + id_DESC: 'id_DESC' as const, + _firstPublishedAt_ASC: '_firstPublishedAt_ASC' as const, + _firstPublishedAt_DESC: '_firstPublishedAt_DESC' as const, + _publicationScheduledAt_ASC: '_publicationScheduledAt_ASC' as const, + _publicationScheduledAt_DESC: '_publicationScheduledAt_DESC' as const, + _unpublishingScheduledAt_ASC: '_unpublishingScheduledAt_ASC' as const, + _unpublishingScheduledAt_DESC: '_unpublishingScheduledAt_DESC' as const, + _publishedAt_ASC: '_publishedAt_ASC' as const, + _publishedAt_DESC: '_publishedAt_DESC' as const, + _status_ASC: '_status_ASC' as const, + _status_DESC: '_status_DESC' as const, + _updatedAt_ASC: '_updatedAt_ASC' as const, + _updatedAt_DESC: '_updatedAt_DESC' as const, + _isValid_ASC: '_isValid_ASC' as const, + _isValid_DESC: '_isValid_DESC' as const, + authorName_ASC: 'authorName_ASC' as const, + authorName_DESC: 'authorName_DESC' as const, + description_ASC: 'description_ASC' as const, + description_DESC: 'description_DESC' as const, + title_ASC: 'title_ASC' as const, + title_DESC: 'title_DESC' as const, +} + export const enumBlogCryptoModelOrderBy = { _createdAt_ASC: '_createdAt_ASC' as const, _createdAt_DESC: '_createdAt_DESC' as const, @@ -5652,6 +6153,38 @@ export const enumImgixParamsCs = { export const enumImgixParamsFill = { solid: 'solid' as const, blur: 'blur' as const, + gen: 'gen' as const, + generative: 'generative' as const, + gradient: 'gradient' as const, +} + +export const enumImgixParamsFillGenPos = { + top: 'top' as const, + bottom: 'bottom' as const, + middle: 'middle' as const, + left: 'left' as const, + right: 'right' as const, + center: 'center' as const, +} + +export const enumImgixParamsFillGradientCs = { + linear: 'linear' as const, + srgb: 'srgb' as const, + oklab: 'oklab' as const, + hsl: 'hsl' as const, + lch: 'lch' as const, +} + +export const enumImgixParamsFillGradientLinearDirection = { + top: 'top' as const, + bottom: 'bottom' as const, + left: 'left' as const, + right: 'right' as const, +} + +export const enumImgixParamsFillGradientType = { + linear: 'linear' as const, + radial: 'radial' as const, } export const enumImgixParamsFit = { @@ -5828,6 +6361,9 @@ export const enumSiteLocale = { zh: 'zh' as const, id: 'id' as const, vi: 'vi' as const, + ko: 'ko' as const, + pt: 'pt' as const, + fr: 'fr' as const, } export const enumUploadOrderBy = { diff --git a/apps/webapp/services/datocms/graphql/generated/cms/types.ts b/apps/webapp/services/datocms/graphql/generated/cms/types.ts index fdf01dcf4..9881ff555 100644 --- a/apps/webapp/services/datocms/graphql/generated/cms/types.ts +++ b/apps/webapp/services/datocms/graphql/generated/cms/types.ts @@ -1,990 +1,1100 @@ export default { scalars: [ - 1, 4, 7, 10, 13, 16, 19, 21, 23, 25, 30, 31, 32, 36, 37, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 64, - 65, 66, 68, 69, 74, 75, 78, 87, 90, 96, 99, 120, 125, 126, 130, 134, + 1, 4, 7, 10, 13, 16, 19, 22, 24, 26, 28, 33, 34, 35, 39, 40, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 71, 72, 73, 75, 76, 81, 82, 85, 94, 97, 103, 106, 127, 132, + 133, 137, 141, ], types: { BlogAiModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - authorName: [100], - authorPicture: [35], - description: [100], - seo: [94], - slug: [97], - thumbnail: [35], - title: [100], - topics: [71], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], OR: [0], AND: [0], - __typename: [99], + __typename: [106], }, BlogAiModelOrderBy: {}, BlogAiRecord: { _allSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - authorName: [99], - authorPicture: [33], - contentBlock: [28], - description: [99], - id: [66], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], + description: [106], + id: [73], seo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - slug: [99], - thumbnail: [33], + slug: [106], + thumbnail: [36], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - topics: [69], - __typename: [99], + topics: [76], + __typename: [106], }, BlogBitcashModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - authorName: [100], - authorPicture: [35], - description: [100], - seo: [94], - slug: [97], - thumbnail: [35], - title: [100], - topics: [71], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], OR: [3], AND: [3], - __typename: [99], + __typename: [106], }, BlogBitcashModelOrderBy: {}, BlogBitcashRecord: { _allDescriptionLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - authorName: [99], - authorPicture: [33], - contentBlock: [28], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], description: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - id: [66], + id: [73], seo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - slug: [99], - thumbnail: [33], + slug: [106], + thumbnail: [36], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - topics: [69], - __typename: [99], + topics: [76], + __typename: [106], }, BlogBitcoinModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - authorName: [100], - authorPicture: [35], - description: [100], - seo: [94], - slug: [97], - thumbnail: [35], - title: [100], - topics: [71], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], OR: [6], AND: [6], - __typename: [99], + __typename: [106], }, BlogBitcoinModelOrderBy: {}, BlogBitcoinRecord: { _allDescriptionLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - authorName: [99], - authorPicture: [33], - contentBlock: [28], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], description: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - id: [66], + id: [73], seo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - slug: [99], - thumbnail: [33], + slug: [106], + thumbnail: [36], title: [ + 106, + { + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + }, + ], + topics: [76], + __typename: [106], + }, + BlogBitlauncherModelFilter: { + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], + OR: [9], + AND: [9], + __typename: [106], + }, + BlogBitlauncherModelOrderBy: {}, + BlogBitlauncherRecord: { + _allDescriptionLocales: [ + 109, + { + fallbackLocales: [103, '[SiteLocale!]'], + }, + ], + _allSeoLocales: [ + 100, + { + fallbackLocales: [103, '[SiteLocale!]'], + }, + ], + _allTitleLocales: [ + 109, + { + fallbackLocales: [103, '[SiteLocale!]'], + }, + ], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], + _seoMetaTags: [ + 110, + { + locale: [103], + }, + ], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], + description: [ + 106, + { + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + }, + ], + id: [73], + seo: [ 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - topics: [69], - __typename: [99], + slug: [106], + thumbnail: [36], + title: [ + 106, + { + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + }, + ], + topics: [76], + __typename: [106], }, BlogCryptoModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - authorName: [100], - authorPicture: [35], - description: [100], - seo: [94], - slug: [97], - thumbnail: [35], - title: [100], - topics: [71], - OR: [9], - AND: [9], - __typename: [99], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], + OR: [12], + AND: [12], + __typename: [106], }, BlogCryptoModelOrderBy: {}, BlogCryptoRecord: { _allDescriptionLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - authorName: [99], - authorPicture: [33], - contentBlock: [28], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], description: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - id: [66], + id: [73], seo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - slug: [99], - thumbnail: [33], + slug: [106], + thumbnail: [36], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - topics: [69], - __typename: [99], + topics: [76], + __typename: [106], }, BlogInvestingModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - authorName: [100], - authorPicture: [35], - description: [100], - seo: [94], - slug: [97], - thumbnail: [35], - title: [100], - topics: [71], - OR: [12], - AND: [12], - __typename: [99], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], + OR: [15], + AND: [15], + __typename: [106], }, BlogInvestingModelOrderBy: {}, BlogInvestingRecord: { _allSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - authorName: [99], - authorPicture: [33], - contentBlock: [28], - description: [99], - id: [66], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], + description: [106], + id: [73], seo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - slug: [99], - thumbnail: [33], + slug: [106], + thumbnail: [36], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - topics: [69], - __typename: [99], + topics: [76], + __typename: [106], }, BlogNewsModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - authorName: [100], - authorPicture: [35], - description: [100], - seo: [94], - slug: [97], - thumbnail: [35], - title: [100], - topics: [71], - OR: [15], - AND: [15], - __typename: [99], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], + OR: [18], + AND: [18], + __typename: [106], }, BlogNewsModelOrderBy: {}, BlogNewsRecord: { _allDescriptionLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - authorName: [99], - authorPicture: [33], - contentBlock: [28], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], description: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - id: [66], + id: [73], seo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - slug: [99], - thumbnail: [33], + slug: [106], + thumbnail: [36], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - topics: [69], - __typename: [99], + topics: [76], + __typename: [106], }, BlogStartupModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - authorName: [100], - authorPicture: [35], - description: [100], - seo: [94], - slug: [97], - thumbnail: [35], - title: [100], - topics: [71], - OR: [18], - AND: [18], - __typename: [99], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], + OR: [21], + AND: [21], + __typename: [106], }, BlogStartupModelOrderBy: {}, BlogStartupRecord: { _allDescriptionLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - authorName: [99], - authorPicture: [33], - contentBlock: [28], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], description: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - id: [66], + id: [73], seo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - slug: [99], - thumbnail: [33], + slug: [106], + thumbnail: [36], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - topics: [69], - __typename: [99], + topics: [76], + __typename: [106], }, Boolean: {}, BooleanFilter: { - eq: [23], - __typename: [99], + eq: [26], + __typename: [106], }, BooleanType: {}, CollectionMetadata: { - count: [65], - __typename: [99], + count: [72], + __typename: [106], }, ColorBucketType: {}, ColorField: { - alpha: [65], - blue: [65], - cssRgb: [99], - green: [65], - hex: [99], - red: [65], - __typename: [99], + alpha: [72], + blue: [72], + cssRgb: [106], + green: [72], + hex: [106], + red: [72], + __typename: [106], }, ContentBlockModelMainContentField: { - blocks: [99], - links: [99], - value: [69], - __typename: [99], + blocks: [106], + links: [106], + value: [76], + __typename: [106], }, ContentBlockRecord: { - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - id: [66], - mainContent: [27], - topImages: [33], - __typename: [99], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + id: [73], + mainContent: [30], + topImages: [36], + __typename: [106], }, CreatedAtFilter: { - gt: [31], - lt: [31], - gte: [31], - lte: [31], - eq: [31], - neq: [31], - exists: [23], - __typename: [99], + gt: [34], + lt: [34], + gte: [34], + lte: [34], + eq: [34], + neq: [34], + exists: [26], + __typename: [106], }, CustomData: {}, DateTime: {}, FaviconType: {}, FileField: { - _createdAt: [31], - _editingUrl: [99], - _updatedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _updatedAt: [34], alt: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - author: [99], - basename: [99], + author: [106], + basename: [106], blurUpThumb: [ - 99, + 106, { - punch: [36, 'Float!'], - size: [64, 'Int!'], - quality: [64, 'Int!'], - imgixParams: [39], + punch: [39, 'Float!'], + size: [71, 'Int!'], + quality: [71, 'Int!'], + imgixParams: [42], }, ], - blurhash: [99], - colors: [26], - copyright: [99], + blurhash: [106], + colors: [29], + copyright: [106], customData: [ - 30, + 33, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - exifInfo: [30], - filename: [99], + exifInfo: [33], + filename: [106], focalPoint: [ - 135, + 142, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - format: [99], - height: [65], - id: [120], - md5: [99], - mimeType: [99], - notes: [99], + format: [106], + height: [72], + id: [127], + md5: [106], + mimeType: [106], + notes: [106], responsiveImage: [ - 91, + 98, { - imgixParams: [39], - sizes: [99], - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + imgixParams: [42], + sizes: [106], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - size: [65], - smartTags: [99], - tags: [99], - thumbhash: [99], + size: [72], + smartTags: [106], + tags: [106], + thumbhash: [106], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], url: [ - 99, + 106, { - imgixParams: [39], + imgixParams: [42], }, ], - video: [132], - width: [65], - __typename: [99], + video: [139], + width: [72], + __typename: [106], }, FileFieldInterface: { - _createdAt: [31], - _editingUrl: [99], - _updatedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _updatedAt: [34], alt: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - author: [99], - basename: [99], + author: [106], + basename: [106], blurUpThumb: [ - 99, + 106, { - punch: [36, 'Float!'], - size: [64, 'Int!'], - quality: [64, 'Int!'], - imgixParams: [39], + punch: [39, 'Float!'], + size: [71, 'Int!'], + quality: [71, 'Int!'], + imgixParams: [42], }, ], - blurhash: [99], - colors: [26], - copyright: [99], + blurhash: [106], + colors: [29], + copyright: [106], customData: [ - 30, + 33, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - exifInfo: [30], - filename: [99], + exifInfo: [33], + filename: [106], focalPoint: [ - 135, + 142, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - format: [99], - height: [65], - id: [120], - md5: [99], - mimeType: [99], - notes: [99], + format: [106], + height: [72], + id: [127], + md5: [106], + mimeType: [106], + notes: [106], responsiveImage: [ - 91, + 98, { - imgixParams: [39], - sizes: [99], - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + imgixParams: [42], + sizes: [106], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - size: [65], - smartTags: [99], - tags: [99], - thumbhash: [99], + size: [72], + smartTags: [106], + tags: [106], + thumbhash: [106], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], url: [ - 99, + 106, { - imgixParams: [39], + imgixParams: [42], }, ], - video: [132], - width: [65], - on_FileField: [33], - __typename: [99], + video: [139], + width: [72], + on_FileField: [36], + __typename: [106], }, FileFilter: { - eq: [120], - neq: [120], - in: [120], - notIn: [120], - exists: [23], - __typename: [99], + eq: [127], + neq: [127], + in: [127], + notIn: [127], + exists: [26], + __typename: [106], }, Float: {}, FloatType: {}, GlobalSeoField: { - facebookPageUrl: [99], - fallbackSeo: [92], - siteName: [99], - titleSuffix: [99], - twitterAccount: [99], - __typename: [99], + facebookPageUrl: [106], + fallbackSeo: [99], + siteName: [106], + titleSuffix: [106], + twitterAccount: [106], + __typename: [106], }, ImgixParams: { - ar: [99], - auto: [40], - bgRemove: [23], - bg: [99], - blendAlign: [41], - blendAlpha: [65], - blendColor: [99], - blendCrop: [42], - blendFit: [43], - blendH: [37], - blendMode: [44], - blendPad: [65], - blendSize: [45], - blendW: [37], - blendX: [65], - blendY: [65], - blend: [99], - blur: [65], - borderBottom: [65], - borderLeft: [65], - borderRadiusInner: [99], - borderRadius: [99], - borderRight: [65], - borderTop: [65], - border: [99], - bri: [65], - ch: [46], - chromasub: [65], - colorquant: [65], - colors: [65], - con: [65], - cornerRadius: [99], - crop: [47], - cs: [48], - dl: [99], - dpi: [65], - dpr: [37], - duotoneAlpha: [65], - duotone: [99], - exp: [65], - expires: [65], - faceindex: [65], - facepad: [37], - faces: [65], - fillColor: [99], - fill: [49], - fit: [50], - flip: [51], - fm: [52], - fpDebug: [23], - fpX: [37], - fpY: [37], - fpZ: [37], - fps: [65], - frame: [99], - gam: [65], - gifQ: [65], - gridColors: [99], - gridSize: [65], - h: [37], - high: [65], - htn: [65], - hue: [65], - interval: [65], - invert: [23], - iptc: [53], - loop: [65], - lossless: [23], - markAlign: [54], - markAlpha: [65], - markBase: [99], - markFit: [55], - markH: [37], - markPad: [65], - markRot: [37], - markScale: [65], - markTile: [56], - markW: [37], - markX: [65], - markY: [65], - mark: [99], - maskBg: [99], - mask: [99], - maxH: [65], - maxW: [65], - minH: [65], - minW: [65], - monochrome: [99], - nr: [65], - nrs: [65], - orient: [65], - padBottom: [65], - padLeft: [65], - padRight: [65], - padTop: [65], - pad: [65], - page: [65], - palette: [57], - pdfAnnotation: [23], - prefix: [99], - px: [65], - q: [65], - rect: [99], - reverse: [23], - rot: [37], - sat: [65], - sepia: [65], - shad: [37], - sharp: [37], - skip: [65], - transparency: [58], - trimColor: [99], - trimMd: [37], - trimPad: [65], - trimSd: [37], - trimTol: [37], - trim: [59], - txtAlign: [60], - txtClip: [61], - txtColor: [99], - txtFit: [62], - txtFont: [99], - txtLead: [65], - txtLig: [65], - txtLineColor: [99], - txtLine: [65], - txtPad: [65], - txtShad: [37], - txtSize: [65], - txtTrack: [65], - txtWidth: [65], - txtX: [65], - txtY: [65], - txt: [99], - usm: [65], - usmrad: [37], - vib: [65], - w: [37], - __typename: [99], + ar: [106], + auto: [43], + bgRemoveFallback: [26], + bgRemove: [26], + bgReplaceFallback: [26], + bgReplaceNegPrompt: [106], + bgReplace: [106], + bg: [106], + blendAlign: [44], + blendAlpha: [72], + blendColor: [106], + blendCrop: [45], + blendFit: [46], + blendH: [40], + blendMode: [47], + blendPad: [72], + blendSize: [48], + blendW: [40], + blendX: [72], + blendY: [72], + blend: [106], + blur: [72], + borderBottom: [72], + borderLeft: [72], + borderRadiusInner: [106], + borderRadius: [106], + borderRight: [72], + borderTop: [72], + border: [106], + bri: [72], + ch: [49], + chromasub: [72], + colorquant: [72], + colors: [72], + con: [72], + cornerRadius: [106], + crop: [50], + cs: [51], + dl: [106], + dpi: [72], + dpr: [40], + duotoneAlpha: [72], + duotone: [106], + exp: [72], + expires: [72], + faceindex: [72], + facepad: [40], + faces: [72], + fillColor: [106], + fillGenFallback: [26], + fillGenNegPrompt: [106], + fillGenPos: [53], + fillGenPrompt: [106], + fillGenSeed: [72], + fillGradientCs: [54], + fillGradientLinearDirection: [55], + fillGradientLinear: [106], + fillGradientRadialRadius: [106], + fillGradientRadialX: [40], + fillGradientRadialY: [40], + fillGradientRadial: [106], + fillGradientType: [56], + fill: [52], + fit: [57], + flip: [58], + fm: [59], + fpDebug: [26], + fpX: [40], + fpY: [40], + fpZ: [40], + fps: [72], + frame: [106], + gam: [72], + gifQ: [72], + gridColors: [106], + gridSize: [72], + h: [40], + high: [72], + htn: [72], + hue: [72], + interval: [72], + invert: [26], + iptc: [60], + jpgProgressive: [26], + loop: [72], + lossless: [26], + markAlign: [61], + markAlpha: [72], + markBase: [106], + markFit: [62], + markH: [40], + markPad: [72], + markRot: [40], + markScale: [72], + markTile: [63], + markW: [40], + markX: [72], + markY: [72], + mark: [106], + maskBg: [106], + mask: [106], + maxH: [72], + maxW: [72], + minH: [72], + minW: [72], + monochrome: [106], + nr: [72], + nrs: [72], + orient: [72], + padBottom: [72], + padLeft: [72], + padRight: [72], + padTop: [72], + pad: [72], + page: [72], + palette: [64], + pdfAnnotation: [26], + prefix: [106], + px: [72], + q: [72], + rect: [106], + reverse: [26], + rot: [40], + sat: [72], + sepia: [72], + shad: [40], + sharp: [40], + skip: [72], + svgSanitize: [26], + transparency: [65], + trimColor: [106], + trimMd: [40], + trimPad: [72], + trimSd: [40], + trimTol: [40], + trim: [66], + txtAlign: [67], + txtClip: [68], + txtColor: [106], + txtFit: [69], + txtFont: [106], + txtLead: [72], + txtLineColor: [106], + txtLine: [72], + txtPad: [72], + txtShad: [40], + txtSize: [72], + txtTrack: [72], + txtWidth: [72], + txtX: [72], + txtY: [72], + txt: [106], + upscaleFallback: [26], + upscale: [26], + usm: [72], + usmrad: [40], + vib: [72], + w: [40], + skipDefaultOptimizations: [26], + __typename: [106], }, ImgixParamsAuto: {}, ImgixParamsBlendAlign: {}, @@ -996,6 +1106,10 @@ export default { ImgixParamsCrop: {}, ImgixParamsCs: {}, ImgixParamsFill: {}, + ImgixParamsFillGenPos: {}, + ImgixParamsFillGradientCs: {}, + ImgixParamsFillGradientLinearDirection: {}, + ImgixParamsFillGradientType: {}, ImgixParamsFit: {}, ImgixParamsFlip: {}, ImgixParamsFm: {}, @@ -1010,557 +1124,564 @@ export default { ImgixParamsTxtClip: {}, ImgixParamsTxtFit: {}, InUseFilter: { - eq: [23], - __typename: [99], + eq: [26], + __typename: [106], }, Int: {}, IntType: {}, ItemId: {}, ItemIdFilter: { - eq: [66], - neq: [66], - in: [66], - notIn: [66], - __typename: [99], + eq: [73], + neq: [73], + in: [73], + notIn: [73], + __typename: [106], }, ItemStatus: {}, JsonField: {}, JsonFieldMultiLocaleField: { - locale: [96], - value: [69], - __typename: [99], + locale: [103], + value: [76], + __typename: [106], }, JsonFilter: { - exists: [23], - __typename: [99], + exists: [26], + __typename: [106], }, LayoutRecord: { _allAiFollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allAiResearchFollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allBackBitcashLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allBackHomeLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allBitcashNewsFollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allBitcoinFollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allCookieConsentCtaLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allCookieConsentDescriptionLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allCryptoFollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allEli5FollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allHomeFollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allInvestingFollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allNavigationCategoriesLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allNavigationPoliciesTermsLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allNavigationTopicLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSearchInputPlaceholderLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allStartUpsFollowLinksLocales: [ - 70, + 77, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSubscriptionCtaLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSubscriptionInputPlaceholderLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSubscriptionSubtitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allSubscriptionTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], aiFollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], aiResearchFollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], backBitcash: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], backHome: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], bitcashNewsFollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], bitcoinFollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], cookieConsentCta: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], cookieConsentDescription: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], cryptoFollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], eli5FollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], homeFollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - id: [66], + id: [73], investingFollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], navigationCategories: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], navigationPoliciesTerms: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], navigationTopic: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], searchInputPlaceholder: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], startUpsFollowLinks: [ - 69, + 76, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], subscriptionCta: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], subscriptionInputPlaceholder: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], subscriptionSubtitle: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], subscriptionTitle: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - __typename: [99], + __typename: [106], }, LocalesFilter: { - allIn: [96], - anyIn: [96], - notIn: [96], - __typename: [99], + allIn: [103], + anyIn: [103], + notIn: [103], + __typename: [106], }, MetaTagAttributes: {}, MuxThumbnailFormatType: {}, OrientationFilter: { - eq: [126], - neq: [126], - __typename: [99], + eq: [133], + neq: [133], + __typename: [106], }, PageSeoModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - description: [100], - pageSeo: [94], - seoType: [100], - title: [100], - OR: [77], - AND: [77], - __typename: [99], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + description: [107], + pageSeo: [101], + seoType: [107], + title: [107], + OR: [84], + AND: [84], + __typename: [106], }, PageSeoModelOrderBy: {}, PageSeoRecord: { _allDescriptionLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allPageSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], description: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - id: [66], + id: [73], pageSeo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - seoType: [99], + seoType: [106], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - __typename: [99], + __typename: [106], }, PrivacyPolicyModelMainContentField: { - blocks: [99], - links: [99], - value: [69], - __typename: [99], + blocks: [106], + links: [106], + value: [76], + __typename: [106], }, PrivacyPolicyModelMainContentFieldMultiLocaleField: { - locale: [96], - value: [80], - __typename: [99], + locale: [103], + value: [87], + __typename: [106], }, PrivacyPolicyRecord: { _allMainContentLocales: [ - 81, + 88, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - id: [66], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + id: [73], mainContent: [ - 80, + 87, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - __typename: [99], + __typename: [106], }, PublishedAtFilter: { - gt: [31], - lt: [31], - gte: [31], - lte: [31], - eq: [31], - neq: [31], - exists: [23], - __typename: [99], + gt: [34], + lt: [34], + gte: [34], + lte: [34], + eq: [34], + neq: [34], + exists: [26], + __typename: [106], }, Query: { _allBlogAisMeta: [ - 24, + 27, { - locale: [96], + locale: [103], filter: [0], }, ], _allBlogBitcashesMeta: [ - 24, + 27, { - locale: [96], + locale: [103], filter: [3], }, ], _allBlogBitcoinsMeta: [ - 24, + 27, { - locale: [96], + locale: [103], filter: [6], }, ], - _allBlogCryptosMeta: [ - 24, + _allBlogBitlaunchersMeta: [ + 27, { - locale: [96], + locale: [103], filter: [9], }, ], - _allBlogInvestingsMeta: [ - 24, + _allBlogCryptosMeta: [ + 27, { - locale: [96], + locale: [103], filter: [12], }, ], - _allBlogNewsMeta: [ - 24, + _allBlogInvestingsMeta: [ + 27, { - locale: [96], + locale: [103], filter: [15], }, ], - _allBlogStartupsMeta: [ - 24, + _allBlogNewsMeta: [ + 27, { - locale: [96], + locale: [103], filter: [18], }, ], + _allBlogStartupsMeta: [ + 27, + { + locale: [103], + filter: [21], + }, + ], _allPageSeosMeta: [ - 24, + 27, { - locale: [96], - filter: [77], + locale: [103], + filter: [84], }, ], _allResearchAisMeta: [ - 24, + 27, { - locale: [96], - filter: [86], + locale: [103], + filter: [93], }, ], _allUploadsMeta: [ - 24, + 27, { - locale: [96], - filter: [117], + locale: [103], + filter: [124], }, ], _site: [ - 95, + 102, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], allBlogAis: [ 2, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], filter: [0], orderBy: [1, '[BlogAiModelOrderBy]'], }, @@ -1568,10 +1689,10 @@ export default { allBlogBitcashes: [ 5, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], filter: [3], orderBy: [4, '[BlogBitcashModelOrderBy]'], }, @@ -1579,96 +1700,107 @@ export default { allBlogBitcoins: [ 8, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], filter: [6], orderBy: [7, '[BlogBitcoinModelOrderBy]'], }, ], - allBlogCryptos: [ + allBlogBitlaunchers: [ 11, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], filter: [9], - orderBy: [10, '[BlogCryptoModelOrderBy]'], + orderBy: [10, '[BlogBitlauncherModelOrderBy]'], }, ], - allBlogInvestings: [ + allBlogCryptos: [ 14, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], filter: [12], - orderBy: [13, '[BlogInvestingModelOrderBy]'], + orderBy: [13, '[BlogCryptoModelOrderBy]'], }, ], - allBlogNews: [ + allBlogInvestings: [ 17, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], filter: [15], - orderBy: [16, '[BlogNewsModelOrderBy]'], + orderBy: [16, '[BlogInvestingModelOrderBy]'], }, ], - allBlogStartups: [ + allBlogNews: [ 20, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], filter: [18], - orderBy: [19, '[BlogStartupModelOrderBy]'], + orderBy: [19, '[BlogNewsModelOrderBy]'], + }, + ], + allBlogStartups: [ + 23, + { + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], + filter: [21], + orderBy: [22, '[BlogStartupModelOrderBy]'], }, ], allPageSeos: [ - 79, + 86, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], - filter: [77], - orderBy: [78, '[PageSeoModelOrderBy]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], + filter: [84], + orderBy: [85, '[PageSeoModelOrderBy]'], }, ], allResearchAis: [ - 88, + 95, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], - filter: [86], - orderBy: [87, '[ResearchAiModelOrderBy]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], + filter: [93], + orderBy: [94, '[ResearchAiModelOrderBy]'], }, ], allUploads: [ - 33, + 36, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - skip: [65], - first: [65], - filter: [117], - orderBy: [125, '[UploadOrderBy]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + skip: [72], + first: [72], + filter: [124], + orderBy: [132, '[UploadOrderBy]'], }, ], blogAi: [ 2, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], filter: [0], orderBy: [1, '[BlogAiModelOrderBy]'], }, @@ -1676,8 +1808,8 @@ export default { blogBitcash: [ 5, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], filter: [3], orderBy: [4, '[BlogBitcashModelOrderBy]'], }, @@ -1685,592 +1817,629 @@ export default { blogBitcoin: [ 8, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], filter: [6], orderBy: [7, '[BlogBitcoinModelOrderBy]'], }, ], - blogCrypto: [ + blogBitlauncher: [ 11, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], filter: [9], - orderBy: [10, '[BlogCryptoModelOrderBy]'], + orderBy: [10, '[BlogBitlauncherModelOrderBy]'], }, ], - blogInvesting: [ + blogCrypto: [ 14, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], filter: [12], - orderBy: [13, '[BlogInvestingModelOrderBy]'], + orderBy: [13, '[BlogCryptoModelOrderBy]'], }, ], - blogNews: [ + blogInvesting: [ 17, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], filter: [15], - orderBy: [16, '[BlogNewsModelOrderBy]'], + orderBy: [16, '[BlogInvestingModelOrderBy]'], }, ], - blogStartup: [ + blogNews: [ 20, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], filter: [18], - orderBy: [19, '[BlogStartupModelOrderBy]'], + orderBy: [19, '[BlogNewsModelOrderBy]'], + }, + ], + blogStartup: [ + 23, + { + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + filter: [21], + orderBy: [22, '[BlogStartupModelOrderBy]'], }, ], layout: [ - 72, + 79, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], pageSeo: [ - 79, + 86, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - filter: [77], - orderBy: [78, '[PageSeoModelOrderBy]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + filter: [84], + orderBy: [85, '[PageSeoModelOrderBy]'], }, ], privacyPolicy: [ - 82, + 89, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], researchAi: [ - 88, + 95, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - filter: [86], - orderBy: [87, '[ResearchAiModelOrderBy]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + filter: [93], + orderBy: [94, '[ResearchAiModelOrderBy]'], }, ], termsAndCondition: [ - 106, + 113, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], upload: [ - 33, + 36, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], - filter: [117], - orderBy: [125, '[UploadOrderBy]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + filter: [124], + orderBy: [132, '[UploadOrderBy]'], }, ], - __typename: [99], + __typename: [106], }, RecordInterface: { - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - id: [66], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + id: [73], on_BlogAiRecord: [2], on_BlogBitcashRecord: [5], on_BlogBitcoinRecord: [8], - on_BlogCryptoRecord: [11], - on_BlogInvestingRecord: [14], - on_BlogNewsRecord: [17], - on_BlogStartupRecord: [20], - on_ContentBlockRecord: [28], - on_LayoutRecord: [72], - on_PageSeoRecord: [79], - on_PrivacyPolicyRecord: [82], - on_ResearchAiRecord: [88], - on_TermsAndConditionRecord: [106], - on_TopicRecord: [107], - __typename: [99], + on_BlogBitlauncherRecord: [11], + on_BlogCryptoRecord: [14], + on_BlogInvestingRecord: [17], + on_BlogNewsRecord: [20], + on_BlogStartupRecord: [23], + on_ContentBlockRecord: [31], + on_LayoutRecord: [79], + on_PageSeoRecord: [86], + on_PrivacyPolicyRecord: [89], + on_ResearchAiRecord: [95], + on_TermsAndConditionRecord: [113], + on_TopicRecord: [114], + __typename: [106], }, ResearchAiModelFilter: { - _createdAt: [29], - id: [67], - _firstPublishedAt: [83], - _publicationScheduledAt: [83], - _unpublishingScheduledAt: [83], - _publishedAt: [83], - _status: [98], - _updatedAt: [109], - _isValid: [22], - _locales: [73], - authorName: [100], - authorPicture: [35], - description: [100], - seo: [94], - slug: [97], - thumbnail: [35], - title: [100], - topics: [71], - OR: [86], - AND: [86], - __typename: [99], + _createdAt: [32], + id: [74], + _firstPublishedAt: [90], + _publicationScheduledAt: [90], + _unpublishingScheduledAt: [90], + _publishedAt: [90], + _status: [105], + _updatedAt: [116], + _isValid: [25], + _locales: [80], + authorName: [107], + authorPicture: [38], + description: [107], + seo: [101], + slug: [104], + thumbnail: [38], + title: [107], + topics: [78], + OR: [93], + AND: [93], + __typename: [106], }, ResearchAiModelOrderBy: {}, ResearchAiRecord: { _allSeoLocales: [ - 93, + 100, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], _allTitleLocales: [ - 102, + 109, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - authorName: [99], - authorPicture: [33], - contentBlock: [28], - description: [99], - id: [66], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + authorName: [106], + authorPicture: [36], + contentBlock: [31], + description: [106], + id: [73], seo: [ - 92, + 99, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - slug: [99], - thumbnail: [33], + slug: [106], + thumbnail: [36], title: [ - 99, + 106, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - topics: [69], - __typename: [99], + topics: [76], + __typename: [106], }, ResolutionFilter: { - eq: [90], - neq: [90], - in: [90], - notIn: [90], - __typename: [99], + eq: [97], + neq: [97], + in: [97], + notIn: [97], + __typename: [106], }, ResolutionType: {}, ResponsiveImage: { - alt: [99], - aspectRatio: [37], - base64: [99], - bgColor: [99], - height: [65], - sizes: [99], - src: [99], - srcSet: [99], - title: [99], - webpSrcSet: [99], - width: [65], - __typename: [99], + alt: [106], + aspectRatio: [40], + base64: [106], + bgColor: [106], + height: [72], + sizes: [106], + src: [106], + srcSet: [106], + title: [106], + webpSrcSet: [106], + width: [72], + __typename: [106], }, SeoField: { - description: [99], - image: [33], - noIndex: [23], - title: [99], - twitterCard: [99], - __typename: [99], + description: [106], + image: [36], + noIndex: [26], + title: [106], + twitterCard: [106], + __typename: [106], }, SeoFieldMultiLocaleField: { - locale: [96], - value: [92], - __typename: [99], + locale: [103], + value: [99], + __typename: [106], }, SeoFilter: { - exists: [23], - __typename: [99], + exists: [26], + __typename: [106], }, Site: { - favicon: [33], + favicon: [36], faviconMetaTags: [ - 103, + 110, { - variants: [32, '[FaviconType]'], + variants: [35, '[FaviconType]'], }, ], globalSeo: [ - 38, + 41, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - locales: [96], - noIndex: [23], - __typename: [99], + locales: [103], + noIndex: [26], + __typename: [106], }, SiteLocale: {}, SlugFilter: { - eq: [99], - neq: [99], - in: [99], - notIn: [99], - __typename: [99], + eq: [106], + neq: [106], + in: [106], + notIn: [106], + __typename: [106], }, StatusFilter: { - eq: [68], - neq: [68], - in: [68], - notIn: [68], - __typename: [99], + eq: [75], + neq: [75], + in: [75], + notIn: [75], + __typename: [106], }, String: {}, StringFilter: { - matches: [101], - notMatches: [101], - isBlank: [23], - isPresent: [23], - eq: [99], - neq: [99], - in: [99], - notIn: [99], - exists: [23], - __typename: [99], + matches: [108], + notMatches: [108], + isBlank: [26], + isPresent: [26], + eq: [106], + neq: [106], + in: [106], + notIn: [106], + exists: [26], + __typename: [106], }, StringMatchesFilter: { - pattern: [99], - caseSensitive: [23], - regexp: [23], - __typename: [99], + pattern: [106], + caseSensitive: [26], + regexp: [26], + __typename: [106], }, StringMultiLocaleField: { - locale: [96], - value: [99], - __typename: [99], + locale: [103], + value: [106], + __typename: [106], }, Tag: { - attributes: [74], - content: [99], - tag: [99], - __typename: [99], + attributes: [81], + content: [106], + tag: [106], + __typename: [106], }, TermsAndConditionModelMainContentField: { - blocks: [99], - links: [99], - value: [69], - __typename: [99], + blocks: [106], + links: [106], + value: [76], + __typename: [106], }, TermsAndConditionModelMainContentFieldMultiLocaleField: { - locale: [96], - value: [104], - __typename: [99], + locale: [103], + value: [111], + __typename: [106], }, TermsAndConditionRecord: { _allMainContentLocales: [ - 105, + 112, { - fallbackLocales: [96, '[SiteLocale!]'], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _locales: [96], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _locales: [103], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - id: [66], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + id: [73], mainContent: [ - 104, + 111, { - locale: [96], - fallbackLocales: [96, '[SiteLocale!]'], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - __typename: [99], + __typename: [106], }, TopicRecord: { - _createdAt: [31], - _editingUrl: [99], - _firstPublishedAt: [31], - _isValid: [23], - _modelApiKey: [99], - _publicationScheduledAt: [31], - _publishedAt: [31], + _createdAt: [34], + _editingUrl: [106], + _firstPublishedAt: [34], + _isValid: [26], + _modelApiKey: [106], + _publicationScheduledAt: [34], + _publishedAt: [34], _seoMetaTags: [ - 103, + 110, { - locale: [96], + locale: [103], }, ], - _status: [68], - _unpublishingScheduledAt: [31], - _updatedAt: [31], - id: [66], - __typename: [99], + _status: [75], + _unpublishingScheduledAt: [34], + _updatedAt: [34], + id: [73], + __typename: [106], }, TypeFilter: { - eq: [130], - neq: [130], - in: [130], - notIn: [130], - __typename: [99], + eq: [137], + neq: [137], + in: [137], + notIn: [137], + __typename: [106], }, UpdatedAtFilter: { - gt: [31], - lt: [31], - gte: [31], - lte: [31], - eq: [31], - neq: [31], - exists: [23], - __typename: [99], + gt: [34], + lt: [34], + gte: [34], + lte: [34], + eq: [34], + neq: [34], + exists: [26], + __typename: [106], }, UploadAltFilter: { - matches: [101], - notMatches: [101], - eq: [99], - neq: [99], - in: [99], - notIn: [99], - exists: [23], - __typename: [99], + matches: [108], + notMatches: [108], + eq: [106], + neq: [106], + in: [106], + notIn: [106], + exists: [26], + __typename: [106], }, UploadAuthorFilter: { - matches: [101], - notMatches: [101], - exists: [23], - __typename: [99], + matches: [108], + notMatches: [108], + exists: [26], + __typename: [106], }, UploadBasenameFilter: { - matches: [101], - notMatches: [101], - __typename: [99], + matches: [108], + notMatches: [108], + __typename: [106], }, UploadColorsFilter: { - contains: [25], - allIn: [25], - anyIn: [25], - notIn: [25], - eq: [25], - __typename: [99], + contains: [28], + allIn: [28], + anyIn: [28], + notIn: [28], + eq: [28], + __typename: [106], }, UploadCopyrightFilter: { - matches: [101], - notMatches: [101], - exists: [23], - __typename: [99], + matches: [108], + notMatches: [108], + exists: [26], + __typename: [106], }, UploadCreatedAtFilter: { - eq: [31], - neq: [31], - lt: [31], - lte: [31], - gt: [31], - gte: [31], - __typename: [99], + eq: [34], + neq: [34], + lt: [34], + lte: [34], + gt: [34], + gte: [34], + __typename: [106], }, UploadFilenameFilter: { - matches: [101], - notMatches: [101], - __typename: [99], + matches: [108], + notMatches: [108], + __typename: [106], }, UploadFilter: { - type: [108], - inUse: [63], - resolution: [89], - size: [127], - tags: [128], - smartTags: [128], - colors: [113], - orientation: [76], - id: [121], - mimeType: [123], - format: [118], - height: [119], - width: [133], - alt: [110], - title: [129], - notes: [124], - md5: [122], - author: [111], - copyright: [114], - basename: [112], - filename: [116], - _createdAt: [115], - _updatedAt: [131], - OR: [117], - AND: [117], - __typename: [99], + type: [115], + inUse: [70], + resolution: [96], + size: [134], + tags: [135], + smartTags: [135], + colors: [120], + orientation: [83], + id: [128], + mimeType: [130], + format: [125], + height: [126], + width: [140], + alt: [117], + title: [136], + notes: [131], + md5: [129], + author: [118], + copyright: [121], + basename: [119], + filename: [123], + _createdAt: [122], + _updatedAt: [138], + OR: [124], + AND: [124], + __typename: [106], }, UploadFormatFilter: { - eq: [99], - neq: [99], - in: [99], - notIn: [99], - __typename: [99], + eq: [106], + neq: [106], + in: [106], + notIn: [106], + __typename: [106], }, UploadHeightFilter: { - gt: [65], - lt: [65], - gte: [65], - lte: [65], - eq: [65], - neq: [65], - __typename: [99], + gt: [72], + lt: [72], + gte: [72], + lte: [72], + eq: [72], + neq: [72], + __typename: [106], }, UploadId: {}, UploadIdFilter: { - eq: [120], - neq: [120], - in: [120], - notIn: [120], - __typename: [99], + eq: [127], + neq: [127], + in: [127], + notIn: [127], + __typename: [106], }, UploadMd5Filter: { - eq: [99], - neq: [99], - in: [99], - notIn: [99], - __typename: [99], + eq: [106], + neq: [106], + in: [106], + notIn: [106], + __typename: [106], }, UploadMimeTypeFilter: { - matches: [101], - notMatches: [101], - eq: [99], - neq: [99], - in: [99], - notIn: [99], - __typename: [99], + matches: [108], + notMatches: [108], + eq: [106], + neq: [106], + in: [106], + notIn: [106], + __typename: [106], }, UploadNotesFilter: { - matches: [101], - notMatches: [101], - exists: [23], - __typename: [99], + matches: [108], + notMatches: [108], + exists: [26], + __typename: [106], }, UploadOrderBy: {}, UploadOrientation: {}, UploadSizeFilter: { - gt: [65], - lt: [65], - gte: [65], - lte: [65], - eq: [65], - neq: [65], - __typename: [99], + gt: [72], + lt: [72], + gte: [72], + lte: [72], + eq: [72], + neq: [72], + __typename: [106], }, UploadTagsFilter: { - contains: [99], - allIn: [99], - anyIn: [99], - notIn: [99], - eq: [99], - __typename: [99], + contains: [106], + allIn: [106], + anyIn: [106], + notIn: [106], + eq: [106], + __typename: [106], }, UploadTitleFilter: { - matches: [101], - notMatches: [101], - eq: [99], - neq: [99], - in: [99], - notIn: [99], - exists: [23], - __typename: [99], + matches: [108], + notMatches: [108], + eq: [106], + neq: [106], + in: [106], + notIn: [106], + exists: [26], + __typename: [106], }, UploadType: {}, UploadUpdatedAtFilter: { - eq: [31], - neq: [31], - lt: [31], - lte: [31], - gt: [31], - gte: [31], - __typename: [99], + eq: [34], + neq: [34], + lt: [34], + lte: [34], + gt: [34], + gte: [34], + __typename: [106], }, UploadVideoField: { - duration: [64], - framerate: [64], + alt: [ + 106, + { + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], + }, + ], + blurUpThumb: [ + 106, + { + punch: [39, 'Float!'], + size: [71, 'Int!'], + quality: [71, 'Int!'], + imgixParams: [42], + }, + ], + blurhash: [106], + duration: [71], + framerate: [71], + height: [72], mp4Url: [ - 99, + 106, { - res: [134], - exactRes: [134], + res: [141], + exactRes: [141], }, ], - muxAssetId: [99], - muxPlaybackId: [99], - streamingUrl: [99], + muxAssetId: [106], + muxPlaybackId: [106], + streamingUrl: [106], + thumbhash: [106], thumbnailUrl: [ - 99, + 106, + { + format: [82], + }, + ], + title: [ + 106, { - format: [75], + locale: [103], + fallbackLocales: [103, '[SiteLocale!]'], }, ], - __typename: [99], + width: [72], + __typename: [106], }, UploadWidthFilter: { - gt: [65], - lt: [65], - gte: [65], - lte: [65], - eq: [65], - neq: [65], - __typename: [99], + gt: [72], + lt: [72], + gte: [72], + lte: [72], + eq: [72], + neq: [72], + __typename: [106], }, VideoMp4Res: {}, focalPoint: { - x: [37], - y: [37], - __typename: [99], + x: [40], + y: [40], + __typename: [106], }, }, } diff --git a/apps/webapp/services/supabase/middleware.ts b/apps/webapp/services/supabase/middleware.ts index 3d6f710f1..1c2985928 100644 --- a/apps/webapp/services/supabase/middleware.ts +++ b/apps/webapp/services/supabase/middleware.ts @@ -2,8 +2,6 @@ import { type CookieOptions, createServerClient } from '@supabase/ssr' import { type NextRequest, NextResponse } from 'next/server' export const updateSession = async (request: NextRequest) => { - // This `try/catch` block is only here for the interactive tutorial. - // Feel free to remove once you have Supabase connected. try { // Create an unmodified response let response = NextResponse.next({ diff --git a/apps/webapp/services/supabase/service.ts b/apps/webapp/services/supabase/service.ts new file mode 100644 index 000000000..dc8864431 --- /dev/null +++ b/apps/webapp/services/supabase/service.ts @@ -0,0 +1,55 @@ +import type { SupabaseClient } from '@supabase/supabase-js' + +/** + * Fetches presale data for a specific project from Supabase + * @param {ProjectDataParams} params - Object containing projectId and supabase client + * @returns {Promise} Presale data for the specified project + * @throws {Error} If there's an error fetching the data + */ +export async function getPresaleData({ + projectId, + supabase, +}: ProjectDataParams) { + const { data, error } = await supabase + .from('presale') + .select('*') + .eq('id', projectId) + .single() + + if (error) { + console.error('Error fetching presale data:', error) + throw error + } + + return data +} + +/** + * Fetches project data for a specific project from Supabase + * @param {ProjectDataParams} params - Object containing projectId and supabase client + * @returns {Promise} Project data for the specified project + * @throws {Error} If there's an error fetching the data + */ +export async function getProjectData({ + projectId, + supabase, +}: ProjectDataParams) { + const { data, error } = await supabase + .from('project') + .select('*') + .eq('id', projectId) + .single() + + if (error) { + console.error('Error fetching project data:', error) + throw error + } + + return data +} + +// Interface for function parameters +interface ProjectDataParams { + projectId: number + supabase: SupabaseClient +} diff --git a/apps/webapp/tailwind.config.js b/apps/webapp/tailwind.config.js index ac9f027d3..34680e6f0 100644 --- a/apps/webapp/tailwind.config.js +++ b/apps/webapp/tailwind.config.js @@ -31,6 +31,7 @@ module.exports = { ring: 'hsla(var(--ring))', background: 'hsla(var(--background))', foreground: 'hsla(var(--foreground))', + infoForeground: '#9395AF', cornflowerblue: { 100: 'rgba(125, 129, 217, 0.2)', 200: 'rgba(125, 129, 217, 0.2)', @@ -177,7 +178,7 @@ module.exports = { 'sub-1-lg': ['36px', { lineHeight: '40px' }], 'sub-1-md': ['28px', { lineHeight: '40px' }], 'sub-2-lg': [ - '24px', + '22px', { lineHeight: '28px' }, { fontFamily: 'futura-pt-bold' }, ], diff --git a/bun.lockb b/bun.lockb index 04eeaf04d249be7c96fd7237f3074f438192194f..6c5f40ec02ce0c9bb6b30a4900f30262d909ba3a 100755 GIT binary patch delta 199170 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

        irm0-0U_Cq`~_J`6Q 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%0AAYRTJj>l{ zE;ZhA_Fb1l)tB#>CdZDNS`T=!t6sM?iI+P2nzek_;{N;#(-`w$mQ{N9K8{ewa>C%(SF0ygXoIzdh%^Nw^{oHrG9(#^@dFB3+dXJ-5 z-&itn@epx3juEy$gXX$R$Qd+Od>jM%!x@aQyV#wDh(Ce$BhMm7FUh6w%!KHW4dEdX z*$`zEZaENrFl$;-FHvfeY~f~XWu~03?v*cEpSOH3bCq{O!MS}GL$0SB55Kg0tySf3&0Bu3tj*~?uWtPO{En2L zGPMqmJ~K6ZisOi^0x!Q!t5%Nq&CxGpmQpv^o{wvdV`r02akJh6L#Wy>)-p%yWGX$M1!TTS|#kfWZ5X|ZZ*dy-*2yK zweaT9jvJ1;c0HcoCgayg{>Q_wzx+N(O1{Bp24!K2n&<9+eraRlJ3sY~t^b=xN>kS+ zg9n6NvmSrE_J@f@#tF|ScYip0NVU@Zc5@?ZjemNwvRP)AnA_=Y?Hf5f+w6VQ{4;}U zeI)r?RBL?(J4t&Udu^!roQKG!@DIjY0bkig5q1{B;sSDvkdYT49I_#fQH&JxixBw~ z!51M$%VCPB98-(%$Ls6-G-B(rGw&7ktopR;@w{A(!$5b-~nT6h~df7<-)h-)K!hs|p1ZQXwFH#;Znuzr3w#VF*=uswH89)2@Ban*_b z?Z4VH{f<}26So$#SJ?09WPjq&%6sWOM@)Ei)SxHJw6e5{7Vlm!qall$+WwkIt&FM( zQ$}-)W`$O&Si+>{!vtHxL~G>;jo%fRqAFUxm$STw?wMVMNUDY`F8*!bz!1z!gyQ5B%x+&7?)d^rk~Z-4)ES=ct-qL`u+{ydsByh zS>r^Xwkyq^JPbbOHp%J3^czFRY%zJXx_oH0j$LomXpr_vUf*4dFFhN1@M(z5I^+K= zXVCIHql^xBl7d^9vU>M${^Icd@JEji5M$YeBx}W;OV8S~ON5|*dJ-gMpsJ3gPxP0#*iCMgPb=2Ca+qPHA_p9sEVrjDluCqSvdFs98 z;(Zq<{-c|CMcQLn@0`tZX9igQUv!-XcofOPb|*3+NN@`foDkd{7MI}e?!ld)gS*Ri z@Wq2W3GVLhwh##J5Lo0t@1*A5+FVn0>gt-Ft{T#JX7kfa>tsn8Jh95o z^htNVm{F!cy}Eg}4#;`8%l2pYi^d&spisr_bMwUuSX8anqe6Qww3~Lk-ll{rd!&!F zek$K<&p3Ibw@7@;M^qGem^MHY?u?#7POCCx{(Z zlP8GaNYD7z`KO*$`O+IZ0uk_>6x}08vCG;eaZ@6TcDu(4QVX8&0Gf^ z-XKm{Yu+FlzeV`GMVzskyhV7wL+q0{XT^Sp2$$&o4spTSCDHjk!tXuek`?qGk@5rL zoWvE&_XFaj#IO&DYt|`=;E#x09}zdKK_3wTpAh#XZdutrA#O@c{e-w<-IAE_8Byvp z;+{3}Got7h#A}HMR(JQHUss4N-_^ z);EdJZ-^G(XjRW+wyF{1ws!2@dQ$eNw9VIrPCs4kvD@Q3g_kzE+-77{dAHj)m)G1L zaO3&QiXXqGxIbrcm97aVcc?gF`utB@f8{NZsI9yAQme*yT9wat@_iXI-?H=A3ogpwM(kd-|dfAsc?Gf%PdDHw=TTpPVzm2Q~$g*sn_G7 zC)1QT7&vU~hgCj9te?tvANhv0{>L+yXPNE=FSdyDXSSJnYxK!g>tg+TA-m7lDjVlY zs?W(PMP7ZDZqu(#C;gLr_jcHSR-U$@O{?L1hb8|Mv2^+Niy_m>U%2`8taa|UXRk8Z z_B|R^t96B|&1=ld<+XKD+C&47Hf}ug_>M|77vG3lb+de{j{{p5Ssbwb*!Eo+XE$$h zb>-|i$(C&HJ+)Zw#%m6HRY^8?m_4Oc(T(o-r!~{f&be~4^XZ6i|82L*xzqhc%B*y^ zSF7O`ZnkNyD%?Lju+Es=KhCUQab;vJD_Q>wQ$4E9`Q+EAYV~I4 zmlWR-F|f-vj|NAJCd!a+?fKXzC#-AjktW=l>TXZz$Xqn+P>sr&Qg1nNuyum1`QD9< z7+iZ&|NK?P-zXTG`j4``?@ji5I_3D2=2PDl-;g9cXa6JhD~&GPW61D?>o1%ub=$Em zM)&6tvvfJ{M0G9j`hE6#k2>thUTs(AYwdE^7}q;j>Xe<2Rrk(b^l_c!)4Tp$HO@Cp z-{%7|<}SOrf7qD}^Sb@Hrr_219fu7&(ruho%7dEO;6csUt@rNsMqyDN_PJixxy1Hr z*3CFnNtPt6OKtT|Laj}Ri?}A?XJtsLYK({QiOb}UbY50dJ#UYc8Jm zlnjx{8kr1HB|aho;hx3II_%CiQ8HOPxGT(RRGZ`6D1lalt@k{IjmwS z5Z;OG@vJi^>}9Q~2R-95Ku{+OHaro@c4Q%09?Q)i(K#`qgFhmlwM8Oj5=64Bhyqrd ztca5m$0Q0_i2@M8NfG@65Jjv*5&_8&!?GcYS-#m2Hzlq~l&~^nM@&eL7@HkY%DN;` zGzDUVFQT;d%@+|V(IOS1td%VXA|xeZY7Rts>y|_nUqq>#h>F(4oQNoi*AkVjV!05Z zsSt~DAp))E5{*+MYUD;#v*zVSc&9=9l&E0^=0Su@Y{)~Kt`)P@Oe|b^V)}I%ENkD^ zfq(xs%kxdT1+k)h(>ys4-0b!0vj5b`dUDpNLv^ZNNmF=a@2Nu?mbgcI1-}QO|0U4{=h$&kxbS3i3k)r$g*hibhuK z{D^?`i0=6jO{`rKHzoWEAevc01rQT5AkIm&uzU+5ie|L?I8L@Xwrs_uAt679=HItu z-s`LhKXe?p=y2Xu&)=muII&yK*>k+sd>g%MXrl%XJ-?O9yt8l7?&*5G-&A#4@{=i! zEKUDoA2aJ*Uw&G}td!&5YSp`R@1}2`T94W^?mhMJ(1vTS)tTKo*1{4EdiRLewL;q` z)f0bz74DlQaz#X7;sXbFRc!dPVe^Xf^29CDr%>Jx-SSxXGIFJ#U9aIO9E6?U(#ls4Cv|9aq zqqX#Y7E)9!N{S%sc~L}Te?*O9h_2SivIy_2h#e&m-K}}W z5aAL&B@sQXN97Qm0}w5gf?=9ch?Ln7-Af_(FAB;dPD(5)kLYiGQ^A6>BWhGY4736( zA_8(C?3ECMtU8qtHzjsR46)oQBPQfTM3hA^WTS!=&4nmc4&ksSsvMCLE0yU;>wN`8 zNN&Vu?RK=)M0r%ngE*#wjkOZ}frygm{|92cbx0yKFCt56#6+uiX+-0Eh-(s)tqf%l z-uV$@%OIv&mn6a^HdI7Rw*qx=oeR*2`_^AMWWwL?SFO)?Je_BvV+9ufIb>qHVb8Da z@SXL3Rl!K#4%N1Y)?2YEa~iKVQ7fa`{9Uvkt^A(aX2byk=b<4LUUK zVpPXG2M=5x_we1meyy5C+D}3+&gZI@H+8w z4|ve^=IoztzSEjakE(t%s%xyzJC_ug+pW-Bj|makzL%@o^y<>{4O)1{DBf)AlrG(q z1*wL8m8pigR_rQ>;6jM*RS=f7OCq2!!Y>dp&k71e+>|(na9`kM9Xi41k-B$6;@LkrAOFKcTHOh_?IvX+?TURK+dm@36F$7EJ| zS&3U=qGbBF!mQ?lNhY)eCQEC~8ZWC)YfR&km}@fYysV6EFy5swW7}Xhcv+Wa!b{oX zTZP-&tGaLUvPQMVbpC@h5p7Ac#mmay4wJGpW^OyoHZSXMnUgXV+hcZkS+m+>g3Dk& z%Y=DZ8wfOciLDmB&TGhUkE?)1R`49b5uI}rfF*{ELSK7XFPc!H;W9O}4gV)XHIj6$kFZmJ?(bu^_>>LMa|fY6 z(pCnsMblZ&A37^)R?%j=7J%j=#RI^0a34 z-~6aN{8!#AQzoaLw{UsGgpoTHhy3g+zm+eX6k5?xLLPG zlO&`mqSP3Khc$5wqDnKwYYDqmY%C&5V$oPcEbH4)L}+tFi(!b^)|z36#w`#&!x3?< zCc_clEfM=9e5}|GM7TtE2O@#BOQLftgx?56A}eSFB4umDhM|Zg)**?L5{sx=MsuDI zMg+G()EI)`|3(;s2xyD=DZ&4Zpn~0$$TAX<+Ip^{PiUu2#v%NydE*d8+jAKtDOF-c zf-Pge-st{$M2ag9l5}m^?0(Pmmue;Tx-z%F`8S(9=)fJchNOD1E&Rjn45lJ z4LRP-%h~RFBxziCvtVa?7-4PXMAc|Qt zXCQ7$e3mF-m7R&0&;zk@CatPeENhY;pcUhYuQvxh4hFy)7v ztpX&-yyZawx~cJ18{(S0tWhP6u~T*B{9L@g`mPekYbh;tHk zEME(easXnOg{Wtpk~lelelA{T`nd+yr4U5$KvEQ%M~X()$a#o>U~V_|PoMRx(ZSpG z62I9wpxopIlb016@73$vz&RPtRLM2#PMYZ}JKyO!F3E&t!)9Hbm(zn81o0#8f4RTm zUb>?ji_R^ask^gdh<;|)BxW^T|L5t5BJ~5GUi0m5-L9KvWQqlAJ|!OZGeh3MxLN&I zl)qfN&ekI{Dot^0{&c2zw}pMv?@qrY;%>tpS$3_9mEg@qpWM+S5z+ruU(=Ww-!9?q zvoKe=Pi+S0J2PxiV7v9jXH*-r!aFb9v6Ssy>dXx@-f2-2zBa8Q5iaT#!tAURzbgh`OXo7TC8qC@low2)~ z9yX6z;IoS}c)Y&z^v>QrTfefMw@z(FwI?J9nUi=m7oEeX>DdQF~Gj6~9_L5eY_Ih`%m#vq**S@gL z1I7*v>iN%-!LN6&=)Pu4%S%O0M;3d(aeb8xN1D%?=Qtda_e7z*N2X4VSbnq4pK+U} zRlYiYW!y4m#_1Z(?3~l@Nv*WiPPLqvC-ledDFYfm>AkPTi+xKUeVDa5%f(4`PrL8l z-6Z!7&&_W>Rx0qlZ+5>r<=4JS({Q_gs(pK-Yr@+(EZ-_-zNsI~PQ0R%E&l2r^S9Ow zO)_ax$3>2)tobVpoN;DF(uA)j=iGUBXV2Q%+t%~GQaSD7b0d~#et2VJ+5zj64qH0p zX;jle(Z6YQcNVyH%!~(ps$#oS;9l8V8wWHBulBy$a%N93Iahhi0Y)^W_iEx8Z)mF!t0|BEYgcCEJ+ADCcCn&0F`^thdML%bk;<>m>Ku} zvT@bw;kC=QtmC`3;LpX0Kff5cJ@mwf6D2YY`+09o;I*O0ia#FUb8f_=z&JMw?Mv|3 zk+|}X&{E!m3mT{Wox?%mnz_boC# zxZQwto86ABo}c5fheycNe75j1_x;~nZ&QEmbZNx?&Fz!q{VVOJ`Zr9zBdFC5G4geI zZkunPx#Ur@(-9u&>lMA!_*$XW{eP?*(mZ|v`yY{ityvH?Kg|E_hLrvt>iw}dL$Bkl zcXdqBVdVRQMaD*MJotR_fv6THV^wj-7#VwZZIV$Nx6D!WN z8V9CdNqcj|NwHdE9__pCn>%FMn6e??ZVxD8_dmKJ z?!bH1HiW<2f2B;4vWa4OzZg5`k_zg2%FsEMwRV_2t`(tPnc=(nc1>}dsDQCM1>LM- z3kVpvQ_#bDuArw?b|FD8Yo3DM)_VngtiVMCeXXSm`dQx;^tb9PCKzC?Q83VQTS5?Q zHBm6g+M-~v75guOAyyj&L# zZDm-Fs4^Zgb~$3K<<=PyB~fUFy*%A{^n`-(RuioVoxqBHok=;-iXDV#JQ2}72r=2( zCE+~@;n&6fKmSqER4b?p>%z6}To)2gw|u)II!{In>x!6Zosvj71(B;8VzxD?8{(wI zJ&Cziw(f}FsfelF5temJB48S#R1d^FYhn+?O^MeM3#?*25fi2(7WG6dvYty6oq?#) z3$etS*9#FT@l#@{71$dQG83_(H)6T_i<%WIvRXHQwC)AVVp8YLe1#dqD#ZX4cxx|n}raL(L?rVs8~*CT7@ArGD< zu`jJsZTqOm@St{|!)%9Al(-qPZg$|aVpSUYoSY%SKl5SeA z;BL+MZTGYeuCix#X8Ld=nfAzKv-v2~<#HL?wCmZY2bs3b!@kH&zr*bk)Gvs@wip*z zNlPo~>Q~3?Zi=K~9+fQ#Y1?%R%H5@J^q*yLD;T{!g$Y%nIBN^rY;j1my>Qt1E%r(7 zK08a08)9);n=NUWTRyJ@9!hAH45L)dYLHs;Wwh+K$6nM~`mi6X>?@oHX4Q_ed%I8T zWYyenzwDE{mCcrt+zNKJ*%JJxj=W8%k5br5L#e{BOQpQh zxrI?F+ubZ`dx~}!y{O7h`QP-(o#`oqvu^wL>`{oi?K3|t?HPMjH+%CX{4EO|z-VV} z%s9&q9xXK|e!5(mA}gW`9J9W2ujF{hlluOVVR&eC)xKOCUFXp|>d?0xJ96fwnjPrr ztdKq(`u6NLpo8t7vuq<@!s+7_IU1cu_K2{>H|(d~>?2<5lu)p)Zqe;1e4{3>($bMCO^H~we`CGAO=QnmZU+HlVv%O3A%%;IP2YW zHXNSAjgw8nRvyvSaQC)7bBr_m?hP9T{oxi}7tRyr^Slgur;MKo6Iyll+b_6BmA87l zvp2LisYp4(mfo-*aP#TVw{8ERZtZOk_&*!ss-}B78@i8sgXqnD8oAjLsVW{s?|JiU zduC@W8E0NQ_|Hop=NZGcyteZn^3nL-Ih&I6kRscuWNP>BY1>$XqwHUzo3FBsKFYk` zIe^JQrV>p%|8I5Z(EB}XTStVw`Au8)`R#}<_|35#w>Dhu z_1~JZ|KjS8YxRC-uPj+6`fT-zj}k{}buF+m; zy%O40d3h}=Z@ja;w(Hy1dF5=pLd``Vgm!q>xibGJH(Ltw4J1sW(mC6>RXa6DasD%> zOB*b3HYuNR{Jm+lf@dDwgUd%>X&I6xba!^O&y%=2Te7a!{@rxUHe2qb?$NEklXG_~ zleyc{lEo3ijLPCp^u^k}??9!tUC9;pE3eloyYqB-RCafE3BHxB_<6maCVN-a-P!dy zn?9N7I15^big+b)uUO5xUc_s@j;pq|wcZu;^7f5cpe}8?>a)h?bUILHfvg$Dy}Ug# z*LDxPu*E(%`V30hQEKTlGoQZwd$noXrf-L24LOuBwlvq}j61Zm+fCi6H>GoS7#>?k zxLIU1(|J1Fis~%1GL1c*m#14iD^W?W6zXrBJ+*V26l|lf2+r>GzfI7YPXlMIb?7l5 zSG(@*ZLeFq(=#}mmG$lq4)9?IcjrftYP?U+Zr%F#ve_zkq?Z0$%zs^!I-6c$y()TL z_0aqBD827;vv|+veADvnJbEvkOz#5{>iuJ4y|;<2cmFtXLRLiIYE(FG{XP_{l=*u8 z>v!}^)f~d02A}pe^C!yxv#gTxvFv=!GimfE2zK(Brq_JqbZ*0pTVR}~Ih={FR5sf} zobn>DU2qt=*d$cpUg-NWuO-H*Amfeu%QzKes&Pw=qbs(}F>aZ0T&gz9xaG#Fx0=co zsh4Agky@T+#n{ml-tBu=kocdm_N#|>aadZhbZh)Mxoj6rBcPF+$P(0kE^Ce!( z^>}8Nk*SciL9g979SMIq*Va~o*FKXj4RPl+Nx0uQKjMQxuLH)ZaIU}qbI`bSxV@%* z9C9F4s`PNhw1p$aWx)My+%e-a;&xNFY9}X5hM9JFyky(jvGw!T$0l1H9VZ6@aROoE*$+!!~WygIs?vimia6gQ@V)Dz0>k`-0 z|5YP%A-kI$Uo$Q@Zjf=;jmv`@X50;&3X>OxgY+$&F2j7#iCR#Lzh}11PrMjTXW+gU z*S}gLchR;|rn(-Ogla^YO!Ys+sV(SsF{^QpO*(F_ZN-gyVq6j2JJVPrj4O)UX53Sp z3SCV7&km$oe56TOocIBg@HtL9=7!yN+_+aJT}fP!aj%Ulg{x`Y8=Q9X2h=t0ok>?3 zml&u0+uoamWr)8u=jo$KSQgjcxKGBF!woR*vvK8d1C9G)Tm@XPabIyNOhp)E+&3@w zuj;P^HI4jkHm)ph+&{)u!KF9uhjF^DpL03r^81NXZB>PrxqFi!c;gNNqtC%OHb%$(lsES!K6!PTti$&<1*m%nb8P*OvB8E)4?@{_{L?&ahGVb zHG#)Q<}eAH;$Gr15au+l8S%YzJ{bvf8P}Y69-LmejcY;t2Q{cpD35V1iT|x5;+5C9 zRyzJCM&>iJH3=e&%Wqs8+!Zctf5HOBwI#0qR68qSL7X;e2WN@vRm7xgPyC{BMU7L} zeaY#l|6)dVM8-FrN^#>l;Sw5G!nn@3q{fxR=^O_^a^wCm>AK)j8duu5uDCSDmBD%F z{C9)PpjUa5usiWy4{uTu+vVn+z)%*9$iSmxr)2PBqgTmP1~sisM4D z^#R8UBWojdBs@c~tun5z$*>>G%{iZX)ibU?@dh|`r}d2+K)eKPMm=H!<8*V}gA%Cs zYiL|B@!NDhbaW0|BO~?Ec^|1)W8(%B*B_l#@29~ZHP#`JD>iSuG;X6p^ANz6$2bj* zDBUoqW@<*G8glx4h@k%{OQ-<}cU^xv1%D&U5wA9}bje!kZlT`^j8#k8tMu#p`UO`5VBfiNb>|!z;kK1C> zbu(@PZo6?kjGKttX}0Z!(~c)WxJlRBxXHL(CY_@X(vFw{YfZv|CgD^Ze@npGbp{(Z z4L8)J8-(N3+NMJ< zM<=w+Azr|w8)MvD+$Vay+Js|``;+(=oL=LMvxxsP>Bbuug8Q97C!f~@Bj;&>66!V4 zxcS5{v2h*3NyaT8e$(t^vT+M>8*p_Arx>@0_-2!CDo!=M7`7NU%V82OK@LOeGhnuH ze-R&U+#KVU;vB}!HEtO$7^lyLKaE>Xe2{ULak|vY;`CV&f>UKWR`OO3sn-IeC!wzY z@+RRDv+-(NMU(C?vvDY{vT@6eTZ0>?2Ec2T*>)}QgeKi;y8As?MjXT^;RBVN#SNZ~l`H(c$n zkdb-_F1ZVe7`NNF-MFI0?J;f-uDEe~aoX`-xL^vnAE$nPpVBE~UPsNg`-zvpIotm+ zBM%@mnS{rUJBaf)?u2oNZ~?}hH104in{lU%JA%6ddYv}zDDfN69nNTYT-zK2RapeK8AQiVASHQ2FU5jgU(or9V{dGjn(RefyZqHqrNNH-xNQl9}oO~PBme-PK}mvOg=*C5?=!r#W}{N|!sXAWxE z6f>8i2Trwm4~pQ_je8n*U+wRO=@{%t9q|L`h*Q@Y%OreAJjgh2;~wF<8mA-DPW}e{ zDQ0!Cag2LRd<I$C z&Lt&IHTV`v(=gSKq%!Uu@e(-o8EK4rPuy{dE!1hGH5q;&e#N+S#(l&+QqH{68~2I$ z6XP-%_Zj!pxQxbq!9^OE$+)k$7tzjP%ZyY!3SOFo{wCo!+-u{q;?!8b!+Ya$nsoo* zJ{p(Hr2B#UY+QbvqCdfnbEDp`AkH~G=NAi_m7rG%li_dT5yq7?PABz+aixq?X6>l{ z6QutGr=7UtMi4nkyo^cbfon}4piZc)ahj>z#$h^!az@&bZB4@R#(Ck|8&|=&SUB~L z>X<4T=Z!mQI-g3$X_nI|+y&BAHZBhCj7jIHVq{$8Gvex*0txl084q`!xH=MD_bl2N z;j#G=S4UElP^Tn5P7O`2o=KMgr-mk1-?)T0wKKgML_3Eq5mF6E2^*S(j2Jn)WpzxA zjMM-8pbkbIQ)A}G>HpcnkbStjQxvlR1)o3(V zN*_eJCfl2Y>2Q-wH`>9t^tdtlQ0LXrxD2>`?D#9LlW`ewhjk8lb;fBQ`e;99To;qh zkr}CPwt96nG7GMxaoxevW z`^HN3ev5G|+Dh{F-MA$tzfw5OZgKWAo%B?W23~_bQdaX3B zEH1#fRmN%3L>6jLeZgwu$`kjd(7J*`z1csZtpaaTkZSR3Ou~x9r?atM`eLed`fDh< zO4Q=l8K=KTwp*Lvfs;8x&t6>KmrkoYQ-ZliHk_4&KeNPX=ljg1jt+a|N|X5*^k zlA3h-Dz0=jc&pEe)fKcAr&Cgs_+ARFD`>k(SBtpD1LSrXSDW}@oWIV$zOxh9>hN~V z9C4U&b#aZ@i7vHpoOV)=czK*|!FL;1pLj*JGG2ReYDWxR+lu0J`5iR*H6*SFUUG+U z4z(Y~wryFEx+agBgpG;&;&e$KGp-47KjV($v~5$I9ylu9DU+@l@pw4BPMdViaX!YK z!|6bnTIaB3As1cG7tF>jiTfLO(QMobm(@7k<+5mN&099(t{T?{R{^JM{F-rXiL0u0 zIbS!f9r3ETLOTC9jBJmr;gr$0*DTsP@K)EjTP9scTw|PGw~gzBYhj#j*R^eD-tyyg z-QUGgE?W@sf+pPqoTsk8F34oaB7~34#$Ac~nS@V_>xLUaF6tj5jO$K(jB!tm>w&9= zQ~#ikbOKvX-l`iHi6d(3g>(GUiQx4Tsc3JdX01Ai*Ct^f;`)r&>z&E4FUzBidv9Dn zmN}=+*9Vhdf0lKs^!jAn0G4$b(?2+DU(Ch>k@`KEuKTYhVKDJ;B-Azj&7>Pd{2fl$ z^LOJ06TgM4O8Af2b_ns?IK6(FbVG4>jQizu)c-K#ej|UIgu`*;jMJbmi_S;oYDl{7 z-HaPSTuzs%yKy6p(`D*m+$iFKw0>Qtp2n&6s_XfWq}|9d#A}+%#LKv`xLPJ%ESyf$ zI9v{sF1ATG9=Cu~r^_#naTACy#p&{kYurTQ%ZzixGZ{`o>eA7r7T+YCOuQ0KmsJAe zrV!Uyu&#oH#!V%@7N@Hqk#We5JR63!%k*|=oJ z&BAG%Q0bE6R133-XUFN4(xjV%%Z$?%CC!tHse3+IEnN$hMgOo6E|4W`3ucKin3!aH~mAK)WsQpacb0$<@9Xi`TjT3l;r18t!l zw1*DR5jsI<2m-G-e8j~f;tjDO4#b6ckN^@wB1i%wXcZ$tvqbK|UAPDLVI{1FP*?9?|!$dPm zzQ9+Af^VQ1CBYPS5DbPPFcgNtTGp+HapaLoT~lW=3<9Q{*t$YD=pNVF|2$Tziv5TO zgJziwh9NK%G{Z#qXFWK=p3n<=LlAU?4$uf1Lj~*5t5``Kfkdl9b*KR~p%&DJLQn*X zLkZA)lYCGB^1xx5%MmyV#~=e_giMebvVcEm{z(92gY1w4azZYRrsN@#7xF=VC;$ba z5EO253S{CCCK+kQD+TJLG_zkPC7{9>@#%AU|l5iyuVL zbR*$8ynvT*A8x^QxB(ks6Ksyn`QJihD{O=9umg5N7=*(v*bVDo01Sj+7zBf12I1N_SGa6V{!t zZgh2Hs}U1@0>6USppg*`guH|IpaGDNph1r;Tpj+96*My^JEVj3kO4A+FQn2(aB4^n znyzygj)SJ@oB~bF(NvuCkP0*fM>B3Tn5>so4k;id_=2Y7JccKrSvODN8AQT! zcmXfr6}*AfJXrXfydT38I08rE7#vT?`9DGAB%FfNa0br8IXDj&;38at%Wws*!Zo-K zH{d4Rf-SHOw!;qC31JWpyI?o$fxWN~_Urr~fP-)t_Hga)h5c{<5_4@Q0bfWB#o;Rc zI^2Mpa0_n39k>hkATH-#e|I)MXngVQ9q6HX;g1LY=F$vd=~JB@htD6Q``-E;Q++t?8Jlk zp!;3j#LpuCX)sw6Ei>841hRmp42^_QFdD|dP%3N?42J&D6}mxp(A~Vg%jgciDa*}3 zGlE)xp7u2)yidLlpbYV{aEkWb2pU6^_%w~$MCw9)SOR~+QkVk0p(CuKARp+JKf-h9 z&e8OOXT;yYTet;xK(mDgv2G{~geTNW1nB#YzTfEU%s|qFkmoN4C2z<=V`u^!*q{Mn zMW_USKp7|jIUxz8fRx}1sUS6^fwZ8hMb)7e)P_1x7wW@2PVE9%2+z34j(}qdZ{uMi zl!oF^3_Rf%C4B>L;V-xb2jLJLfunE?j>8F<4+~%(3T#qVKkk8jupjhLwl55Tp`eHE z4y)`JZi9Le)#z0P$V5}f41SOnG}k9D_ z;5+<;-(X|?Wx^|PJppa~1`*BAxdpf3K0JVjp!qnOe^U-LqeU}V@10~=`~43|HIb-ciQ?PB4z1+$HE{O3@dQ%gdX4t zc5nkyCiNK$3CIK}umYt4=0i6GaTt^|S9zg zOa@=zBYc7@a0yPs88{2eAq4(}sW2C2z-*WX+2IWbqsOp%?5anqpFvMi_0&{P!p~}` zOV8{NfFAs7dPE%Xf%u?V3rRskEsa3KsTw|Q3K|}54jTPz39Ue*nHs%p3+vv3YB!X=mvGhimn zg4qzocHiJ1xD4kxH8}{=Q(z5B9oC@Y5h5DiN)2ft8Mwhrm;s*P1+l;#JfIGgA`?#< z-%HwzCJa1>=`aH(K{7}V8L1IHx*q|Wv^okj&VLxTXi{M-A{x8bSp7Jd0Gh3=G5MpQ zad-{8YnZ(>lmQKq>vtUb-G)ZNHR`QV?hl|}MQA{|9@K}1puuE~9XADy1OK2<8q=+< z{=XKKhH_8C0zJD;Mn;=R-;FB$$oUVWV0)ny%YVRTPQeyf z0HJV{TJwhh$Obd19k*Fl>}RoZI5LsXWVY2z+G*rF9cC!xU^HaA5;P>c8bU$;ceO@i zH^Ua#3fo{i?0{u(nuyo0@B|{@DLjKn{&vjwSmmsQ|HKM*?BfVDRI4G^O5hLKAQhy6w4f1Cjd;d~ z1dy2{&jtG7%1@{X^`HSXghrro#U{`cT0l!^3++uD4Iq=q!$2Wepona2SQ z>HP)`*$svvFcdVXrLJKFXh3Q-w1Ae-3R*)O&_HNM>(CEwi(I zLw3lFpUrM(!e`FV7tp|)2HLK`Avg*eW7`87S8Gj$4kjD|ona@-`3Q4DP3o!+RMzaQ z>_jxql@oG-2Y7-VydV}VV#iBBKUFIY`nlm2*aibYzXrPqS0E>7#ApZUcflUm3kTo~ zT!w3)L7*fW9oDddh7mTwX4nE-VH<3R9k3I^ARII#p)oRz4C#NjegzLfBR*%KHb+_q zazQ&tkE_B#6odkxL7;qaT7}fF12x+7H{64);6=%%k@-{@2ye(BFB#>7{J@}!tsoSF zDx}qq`gX!1&^XE?D9pMduz=f_K<r!hJv|{s?B^^eaQhRyycfT;&4^ha)?Y zusu|RV&F&Ow2%%`LMliN-^lm}co4Tk9rDo6+%~{?s0wwUBl*5&U0>)2!yz-If#*8^ z+1SYuN_Ga)L3Ys3lHP%SinIcLkmemns$U4{7eS|}z6%hW<;36yUa*k^*h7u^kajxD z4$$vpmg85zDp(Dj$*%#F*Yk`pb`%b~pcd%&EuScP7c%Jq!{9c{58!XO2yLMeG==(* z9MZv`6iz=s(XUPP>yjO?6DEN>RDq1}gR9Cp>a6c|n!mCMG{s~a?1Wvg7y3be7zmmh zH3){lFz60BIST!bA`YQ`C!ya!T!K4r7aoCrvak{K>jVATzyTpJU-4kx^fQ7IP!j%t z(ohEUQv&@;paLYKRuV!=;u+bYs!$X3RXaIoXz?HeXk7woAt5A(R&bY7bs3hxV$i(# z0&q^h&Iu<$Iu0+L)grT&8*l>t?uCQ>W3<9(=dKaS4b#2>TV z9y&rN2!bxq721N<^?{x-;>(GzgjKK*7Qte0Ea6R!VGN9gaS#Yqp&CqP$4Lq85DUEF zH&uHW-=6bt(>aG{)XLv*6XvkaqNYM%8q2exAIyW&RC@?v3BrN zev!8u*Rl;B667Sz4iAVwf^gUcyI~LPg?+FensI*Hv6CR^21ChjIE;Y*-H!7gbHmnb zsL8qQ;XRG02VqZ$*$7)wa_59w+RPO=$NDFn!l&>I?!kSy3%B4poCjY>1!*81WPo4J z+4)3jL(H9&Fy~+MT;IUI&uPqMO=pd{tf{POGoG9lJ9t5FYGE)82iGZ4=+L>*<)X$m z0mgIAn&X;*rkoytQp7*v_3Pj-pvjd{pt+N^*`^LO08MGs6h=)?)O17-PL~(N0&j>7 zali+vze@VnuqDP#&u2iR5dk|qq(7Aj_HQ#}o5J^qF08M9l1s9|1K+y`A%~suP-8z+VEijp1lr1=0qZwm6N9R7;w2sF{N)p+4#M z;x+S6GlY(UCi0aBP0G_GyeyzOc7C8abcsRJ;rv0<-2yZ_PjlQ%(E1reT{%I&>Y%>L(|AKZS3Ef+i<0+wo;H6 za)4?ewn?jrT&f*S-qN<3u%!uFH$Z<=FJ_@tGfJcXy1?a4=x|P&s!Fv~hg$IOf@t~Q z&J#BuG#6?yX#UeO_;)(hVtLS<7*{^(&O<4PrZ}zD{|=xzOq#QF2Q*FT5@>3YrXneE zWKOtH=GB={KQL4gB}N()}m=FlVCEawp_iUW)W#QQrEx!^vVm+G@|M>z}(;} ztt(U&)}?l?5nhKv#8o)w(Gac!9c5+E(aGz-FL(gTL)*Ky)l4E?8A|VnM?|xOVu7kv zvw~)VCSz$fkOi6_rE?z%Dy(Mu+-E!OYzd)uruACZRkVck^FXt~bd;K)QV^AzVP3p1QEb!eISt=qA^{WIu0vHoL~ysR6`k>)4N2YOtg%PyytD~@-gN;yc9 z8>QR*Y@mY*0Nq6EtYw1qps!AqNT;t+sX$+?MzfrfFv`O^AICeRLwEA=LC5zDR4kQS z$ErK~`*08Ry+_}7uET!NH(GtOT>_&(vr=MVgY1wE0zm(Nj&`E;I?B|bWnV}ISs^{71AoX2 znIHqC1wSzV36C>K&$7~rjO_n^Rw{*7WP$YHife~zZkefxRPdPPl!PguFmY|C0;=G- zASdJidA+pGbuiK9_wU>|hZYJEDF_8Xv!U`qUdRJFI;Bxus9>&jiff%-8Dqpt5LW?v zKwGE;#h@rugbGjuNawJXCh`YpWhp2LN~}zZgPbS>WkEYDPgo9=#WgUQ)mK>p*qxr8qfgh zLv5&K;&ln@uz%+-qZ<-70%fR-Z-B;2FTn*k3+LbzxJuuhxC+z(+Cw`~ffZNS4LU;? z2!c+~5#(H9bFJ(Ot}2gN8*)9NH}nGSOx1sq@DTKeaS#jxVF2`lzR(A(HVM3QSiKT> z$95>GR!MJ#P#6X?VJJ+4i7*3{Q~( zci5&AaqUEfnF>>2GAQvR7!S%w1(^UUurk*%TZD697R-jZ@F%Q-m9PR9!y;G+^I;x@ zzyck@aw1D%3H$}iU^Q%pO|S;m!CF`k8(<@B0j1IUgKz-$!#>yxdtf*0f^Z0fov;J8 z!!|gnDpckt;20>wqi_TcgLZlxTwcqaLCx+ooH5H?2+#j3e$gzud`FhA!!@`HSHN+T zw-TUs{Wm;-KD1^|in#Jtf$xFxRvL2< z4(EWItAsj=NAM8J!ZUaR5%3fOAUfOMzZ1Dd-^)khl*Z2TOTriM96VY6P52GoK@@z1 z5AYV=z-yiVS47@}5`BTs@Cm-cKkyxXz)$!E`u?YM%1qw}6>8a?xV~WM8E8U?3;Keg zFBkg45f5BWUp^F%qlcreZz2k{u|jQ-09+gBTZ(HVWg0VWGM1Bq?(>p>mR<4W#C2Nq zk(8TI1HYLdJ!ojup)u0bkb{-=p&q1TIW5#Co{=yEXvRZlC__9O=r%nMVP)ts|IS}>RA)gO*CDJ0H9-xl24Qqdr3Q%qUk$Gz zz5&!Xp$7dm#Hk%O!JUC6I1ShgA+CL`WO*c^L$}B}$J$8u-)i;CiB}}<`nIx*;~OI>w7{k7^L$b zOk^M|CgA`=eMs~t><4{eIm;T;a088L41+(xRp6P#XTUTV1*+B&pi6rSDBVOD4?2D0 z2*<)07_CG~Fd8PnAW$JD!DN^UYJf2>#p$?NppI!a;T)I?A@G0IqzdIK>_JYE7pFn9 zN6N6h!_I=9c&>xZBs>T6L5=1Z9EJU`3l@UffzmB7%YP9rfyJ;0HbE$CgblC`*1{TC z1+H`}h%eLizm&*w&_+7a)r3m864rxC@2XzcQK*Vl?K%yvstzZv#=DhJr%Gvc$?hcF z4%=Xd3Bw3?!y=vky+roFJ~#r0L8VrLLvRodfY!^O03GphvwYetpCWV}-AUp)sIv;G z*^7j|2+tELt}E#dVSs~3BhY|*YghyIArw}F9z5xRav>-P8i3b8yawd+fClI_P_NP9 zoR9;uLwd*tS-~H&Kqk=agLIGRM0m%|F4NCGnHEBMk`%MpfC!)!UuQ47&5UO%-(1wb;LY*R4 z+!bm^KA^!Ums6-6>mU;0l}4i=dNvdvwBGUWqjfF25-O20Pyv!b60@OPQsT-~X|$c| z+{Y|z3bQ_@Q+lPv@wh!Y@}E!*O07Q4|7SyG^oBI9f@oQ-{oncMif|oe%$;ZcpR}C+ zm@Cy5)S6wH1^iFq=(MV4*ZP=omve=#t3?N-eW=D<>;4n!{JRQJfJCnNA2GZ>R`vN^ z9k-HDAJytq`hczwbu2|d4b`fs4Z1=X&}j^Uw%|~w(gZY$)d@O6Lr}w0 zLsNC>ROmdaO4YzRKzmSUqrO6&Njp%dq)w*|s54W)qE4z6w1gJW9GXI7Xr%M69;^YV z*HdRx59)$H)PdS?fupHKs18ZJn0l_7pbkqtn%YZtP^VP`@RK+&vd)*QYSlrN#g#Dr55iJV5=uiE&^F3P+v&7u*_EeG zv96YiTJJjluEwI3G2{PfC~Tm0uI2x$WxLktT6e`=p{r55E@dsN({ubgLCl@F)~P+X z;;t}e0n~n6-W4`8&efkNU$r&YdUKGD|2Znxk>93du9CWXE>*v4W3}A>-=(KK{;z{@ zZTr6==U+m&cIZl=h86QDUEUSCuIJvQTLeSMV?Nb+ z4&M4hKk$dXplex2pv%v-GZjRapO$sxt|J^|;=0}k5?3Bdqi)%iPTK|>@7m54Iuy~y zSxo|!awrVI%_N)vQ(zRRu*!TS;Ru)vT0a`bz*ra$<3Qh*ClOAB=`anZns5f8^7&KO zzs15l2mx>8Lc#^09cXzm;Sy*_hRX6E)S>8^#4K~1f(9Pvm!uqV+Ot=X)f(olV zwh(Ha(rLZY?gVz}bPZTnU)R5O_LUX8;T;@={jdjifj$egPV9rdCVs$#N~gOd9YoB# zDK$_X`EF3-d`mj*ONDzw{1rHqsm}QgcmXTmK0F8Q>^i70*B~6Uvs<7-sBp*O6dZ%2 zu$RosaD=M4BXAh>t>+M-o)PIoEJi^c(IsT1jymSfF0ri4m9a8&ZREPiP{HN@bCkqY zcojzeBxvU+z*T!%uROJ0>rSixSAw%}1}=gc*agD#pnLywgmrL=E2GPB6-MK(5bCOM z-S26=3aU^wsvX^glcZI(KP9eps&QvcYh!g1_Xw5vHXLU84&hyR2x>t3Qu=`SFf!4( zj3B-i_sFbQhVn}Lg!tdEon?LNc}zTv@EKtwyaXNOYeI)M`a!S9zMbcP)0Vs zBmO4*0a5TBzQI566BfcRa3`H>08&GDIzkj*WCO5gnw5b8%qa+)=q$i$NqYQQ=*_<{zoQ-GXnAY1#{54sX| z>HhyHku9KW{EP`zx~9ZKzyfWgf@$PZ1`TzW!%K~6+%4^3})RWmRl2V z4|-^!=@{*xE!2WGP!nnZ=^V~PS|O$IshOMCKx2Ku#;w}zSjMm~av@Br?^QP=}hU>Yod z`7jmc!Yq)V0n{T!8a%4$i_E zI1Lok;Vh}DNTt?!R;g5al~@zSRO+vw^yn)v`|F4K>!d;+($y8Yo&e(m=0q8Dp0rDnQ&@w z0fhdL6|%u8GIiCOYDV+HHR)V)!}XL@bHz26Nb|LX*e`kw_mR#fcY# ze?OAfoSnX`A4=xg37xl9WTa>P$6zxYg|eh81G-%QAYDb`6`(vc2VJJRY@2DCNmE1< zXbg>@0n~#!Pz!26L#PjRp*GY6&5WuFflvjifqF-!*LK=o`6xf-tNgVecha;V^dM}h z^WO^ELSN_v9YD8B?Fie0K4P`3up`TTAPGm%o3I;nh9Kw)U7#2AgdWfxl+NW8y5d^r zP(o!e1t!2i7y$jCKMaB4Fcb#EAP5GXnnj@X0||!_&ZA+CAk;yP0d-uX2-U%jB&^L5 zk2mY&M|;}bZKJH-dAw6NbUKvBG?)roVGFE>wXgY8c+ z3x_^S}$AE``2Y~y4yMU_z=EuBm z0OJ3~KkPA?F(a-cy#}}gxCvnVZ9tB6eG4fA3w;kM=i&UEKEgd0_!Pj;J|X=G_yA}D z4ZlbF4zLOpe2erA`yuuzuW<7c@B;80&<)IL<2gr5{1*K+p0z~EbWM?dL;4lKg|gs3 zrE8w*l`w_}2Jjxfq5vL1O_B0z0}WC&pfJD$V9eWo3*o{DpaLiXEV=wA7r^aY1h`@V zerAF6CZ3riEh#dEz0t1Bl84oB?G3d}>2k08i=5 zA?3}F)d0N1kq0&2Rm)w}73njitgTRhq*CF_s>;B%$FoXEJ0NAvR>U>m^WexlnO87x zMbzecz}HRjEEv!Q&>0W{XaWcT_ycMHd;ofYH-MYY3n_1JvW&L^hw8ZV1NZ`%6Xy;D zuuf_stqW)fs12wG2m&yUpVtD&G>qqGbpX7E$F7s(LDoAz=VyH10N3>acpfhhb4F&w zM7bM_J1h4ee%=_+NTT6uekNC)t0q^A1>@&jZMnMqjA`V`HN!PO>%^jg^ozu z1KI)F0@?sr($;`hfR=z3fX@)>K7fT|oD59EdAOf-K+3h2X_!CbnXfDa3lJQS2dwU{ z0Di!K#c=J43~ai2P4P3BvpJ528he7S2jDK~x+4t(=yA&;-yJ z&>PQdAq_{$!tkHWHvrH50lk0&|M*|bWu^{5HUx;-#D7@`j(GlU+T0HeG!WWXV4b^%fX2vaqr9IlNA%*Qp) zxQ+tm;hH@f_oCT=S%4XU>42$#8gQfIkv;;=6r{r-KzUSrBCclxHvy>wp7C%zi95k$ z>A^Ilyo|?8n7JbeW+IiPPn4c>ZRY^EAf}rOU_s^lt8u>yuoSQoundqU-PZ!0GhhYo zm-91L7rQwomLUVzYXD0C+X4Fl+W=buk3hQ)DG#AGxR(7s8@V-L3vinN+)*|nT@T<2 zFx>{}xqQzw@wsm{gMgRh9H~UlxC|~&=mqh(KsWfU7jO+&>by0}Pg`=A zlADqR$*n!EOn!V=sCYI8mgGKwYmr|~3o7&7PeF!SP^AuY`^y@T)hDYiugac400n@%2^#=gI!h47GE#M8{ zHGl`J=SX=`{1wudfER%GfIm5`Ha5ZV2~YyQ;i25;O>wQjvmZ#mOV1g%1$kr~KmRG= z_!;BnRE4}UuR^$H=fyrme#U&_nOH7BF5Cjd=78dWTVTjByeY2Pnl(uIB}ieUHpm+Q zyc*YLfMNjtI;#kPYs+{Z+&CW>YK`aoX&(pP-oW8LUI<=m;D*($7a#s28i)I-06tNI zPnM`J-Se|(z#M=)3_Akv2|?eWFrGp=U zPz74fh#dR32c|7Mp!4<^3z1I!TtKgb51p?AhD_#Ii0~`VH zgffExH2gZZE@=D#OK`mkkP4`UdtU&DcPjwP0P(z>cN`bqDAWhYrMON3=yB~0SdD8h zq@Do&M3d>cFkY_9&77GsLs_^Qpq1&?;5iFZoq3=fxo|&jeh!bIbK0Jr~aKq6oWU=|<(z@P7q2aE%Z1&jeC0D1u80I`6PfDwSFhO`A8bOq07X z=26i7DDMB9iH%7n8jTDxBTnV#+*C4cZl+v&nO>&N?>;v#E@L8Kg3Od}CIeWINl0e^ zrURw{rUIq__>G!euv`FZN7e{GlkF!zW0P@AFB_t;thxXX_&eVuz;!i+dM$Iqr@v0dki_SB)3C+G0H157-CT23P}F z1y~7K0ay;;i3blT%W%CEkOtt_I!lnI0(jpDk0Jb=@tiju!1&bwInO#=X8_g$wsQOP z%@zQwelyaIfC*r>0VxylA~r8jEAVVR?l%FLb}!QHfIR?yZ@3fb4!~}}E&#jT14vm* zucVZ<^#X7k@CY!3yVxzjDFC<6aR96H7@#~D97fsr8-VM8YkdH4`7K9YeC@+u8@hi7jAYzt#i z4yR1R$2C+3I}W)xrm(`jCEyV7C6THDd_ENiE+3HpJ%GC^-*03^@BvoF0PX>dP~iF} zh`sQ z0Uc{1_q%EiSh==U0bD^o?7|jc4Y1+!^Qr(U0QmF^M}QN62^q@cx-7t1O8K;masWOK zjE@DY2yg|&qE_rTbhze|vqnLvYDnDxd}J8k^Ql_AIK|g|I0EyJzkq-Et%DC990CJA zhmY_1wLTBcfw<>aG1kb$fhZr@!CsGR~{8gKpb3C^ zyWm+_0Ec=OXu?)_-hxjlY>Eqhz(jnyMhKuefbZpJe69pvkCNzIkh%gA@SKI_LrvNO z+5m2%5S~A-1@MYlC{lSKb3z__-1AJx3Tgcw_(z@|l>(CI0=7uy<+V)Qv%2|DrsMe| zKj)b)uN8<`1HKQ0;K@gOq=z8@zkG0tZ;dB>*BeiJ0m1=20X+cS0bzhpKsP{FKo@{~ z%1uXH^8#T9q-?Q#M9yeHEMOF1Bwz$!IA9oHC}0R+Fkld1AfIB!=j%iRq5zSA0e}cV ze?UI~pG3qv6|11}T>xWnpMjKh%bP~;0>UjGxOmZin4?J_#@punVvXun@3_ z8+s=$(f}ENZGf$SR6r8I3ALM#bTYsnOnD|T9oJI~LwE5Jm-u_~ zQ$tN!s$HN+j0ONRxW8sKW_3wi+;g0hi3U%K;fcGH_Nm&^q<{3*04I}jcv2J?-lqe9 zn6N#~)b`2=w>wTIJTl_rxrC8Yo~4f;hhC}Oc&w92q?Ct;H0Ju!s#Zj^_x)p?OeS&p zC_W)9I=X6jB}1T zTMZcFWO5o$iXhKxq!vgsf0(qJI?vI_$>ctsu-L`W^2}8eU8LowVG%W5oJ>@B#F^}o zvKE%5M5M$4PWoi~Ij^ILG*GA*IgE-Oa-b zt^=6j;4rQH!X#2$IKmMe}!o4bGoZijD&rPTBq1&xd zi6!(EiI#VWu-@1GZS8&NL&*6;zbbpC5V^Ne+j{fH2#iq)HLI>s_xs!7rGes$ggLL@ zTKnL+H(d>w5;VGvx>V`PaIvM4b1m>pks2Z1#*lz>I!&{J>PpD)OK7VPyXmYzY9~t8g z8Qc{UfngCoRNa)&{K1W3jG;Si)z^K$d_ zQ_Mkbwn_J>KaywLjP~Z-XixA>1BPwwUQpc`7f#OiXN(RQZ{cK3&a)w$%)ZaKPzD$s zxVdAFEJ@k;)2RsK_?OFD1LX;n9uq*I*rH;LM>Gk0eL0ocL4F_1v3%7jLR`G4D zE@V_`0F*h>xsicD5OpZ>%ZWB?lN(m9<+B7#5*Isfg0k z&tI4(Vqooxw!m6l(yM=Q6SPOj49!xAg;aL~sYxd(Cj}!_*LwE~6AB3q; z!Wio-S>emRnF8bC<)(vD`PU`zf5L|Jx06~f+jqVw%2{-gaMyF8Q7IT`>;|szr8kYY$2-f+B&jM4nNjul>>6@iesjrC111R=7Op|z0hPK}cRVj)bm)ZZrGjp@A6YMIX`30&@T9}h1F=2<47-8; zO6_)4MOW63yJ*9FjygAye@d4jMTSk(00Zmxf<5MSD$YvDrw9ksa-OOiGs?8^pIj4p zyxroxQC*KOV8;g3;kD7yTUYgJK{`)XH~;tzkO?LFTwGpAb#ICwf}Tyw3K&-E_ZjQ2 z`KFh!7V@Ag>lBrNVQvQx?})Bt;r`WG=?&Y~p?~4zPr;eL)pUBV4nKi%;)M?8i%`9< zP-VsP(|i5K_&&V|cAhX3Pj6ugn0vf*HkkxcE;{dU>0ID&>HKTB3>)4zY7p}KZu*{h z;#6|;kAmDjT%!<3hyN4KjyLvOZxOga5P|!bq5~fDC}7)3b?Ew!cm05YS71z!h(SVI zwYu5qZl@awMV#qiC%ASYtDXsBkktiQjcwxo4Bxc5DlqUutlA`CxIOGmcONb`B`g#e z^f7kWYk}b!d#9ar8FR_3B`_WyZfFmm-UGv;`@}>jO4l9urH6B-J!AB4+@W_N4>#0; z*BzcF6`Esp#+f>G79|me4xI=8k_h|Yql(9^-puG-8F^t%=)mshfZ+zau=I(}`L0I* zbA_G3x!sbu4m9rn;89Q__cnJoVYrD5ACAtA8O^D8S8KNHy4c_f9!q(&MfX4L?_gmO zCuGvGy50hX{}X!bNS#|#>92Aoi!KLQ`ixf27rO{NEl^X<4fO3 zt$P``>40G;U|M_qibGW`rEbgYngGM&OHjI-$D;VxY3@Qn-VjsKQ9`|0SiZN{`mG&> zf}m=+R$y4Tz4v{R-d1qhFJQn8o)s7tA?U{T{zv9jQ38We9YenG!-_0+zor9bA8_|N zEaVZK`yAw9OxE_ouRmPcw6B{pjqa*0X&1^`XUxKXx?1B~XU#2ePeat#e1*dsgw zGv4qa8)uX&cZYf7IhYUa8$N)u{#dxw=)ha+L(b$_#E?@H z%9_@VBO=Jcb|yOpmFO72L`!AD)umL|)MOT;v86ZN)Ly0UOFmBaSLVvV0Cptqidd=@ zs83Cw|QFgn!ACy~Zrl>psN> z46mj_uqS93XO1V8(pZ{6VfYOox*4jrEp3Wchw>7JobLR|K7F_;6ARIU!kR3?)JY>saFqmiE_>eym=Jv(S2CeK|T8`OpZIaT#xvWqf(9q#sQeL5|o0HwCi!t zL*nhrcTbkD-ZxUEWGBZ8Jye3SS(Zzn;Ca#3Hg4&sGp3D#?>UX-E z{X&Hi3yq47WLA^~`suiQLr1+j{KW#7c39kI=DbK^phq*QoZ3&qWstHD< zjHTy2)t*Yhv^>dcvc{a^!quj>dBb5#*cF_pFYO6eYvl%|SK;aqr76|y1^*;8c}ZvP z^>_>MThmJ63^(n}UQkBCh9-%|jW1B<+YAT?VVD}4%zK-Y8+pYtn|D@0qcLZoV|#L5XOG>uT}R32IiWR7*U)d#q0ym zga#Dcz+d(h5{)|Dv!^PNpnL^N?!AGP?v#9%{>1^5JV2s@eYdAjQ0R-oy;y+au+g&> zR(4-^2o!?h+XKVK>G`5{FO$0clYx<(jfVqmKwedS2f7)jvDJ14r7$B{UZ&MfuTs}l z%Ap(#qkEllpl^|=PLLzHN1>gY!e)7#Kl9wUeXa5b^@8NWe511?#WBSTP@v~0?7i@y zd-|YllR&`}1(@15N6LyqsdJp@K@_Y^s-sG-^MP`7ZXD`-s+>5a6xnrQtantRk4otS z1~4|orE-)4?P%|Uf~ClC{66~cMh&4Aq4R*^O*zU2h299EBx_~RhjI&+yU)x;VJTrY zA3J;Ab>z_XVqWI{-*)8ULaF0X16X1NTU-Em@ydn8z_`jE?-|WB$kE<~(m-J|1QhK5 zz2*=488o1QheScI*J`XNv9DUK0_DXR=xt_su}*DE?T9V@@(L}Z@UZcvov6Cm#1jtRmr^a-b=i-wOz&5 z^E^9f`nJ&EeFhGqqtN|-^&!~_Hm^ud@J-qy6~!1N-fLmZSF^{VLTSRRJF6m@4;D2o zJG9FcsqSF)Le(mhYs)@7*^?@<5N4N zKlOjbdEw(ZNUuykIB)sNqROc2w5YPpv*uYUt?|ShdAyLayGiKQc~;@&*)D=)7(v|? zjVe>{P)ODe6sDl)JuIQF#;<5^fkJ3>C?H`dItgldZYa#-c@@z-qFxQFYdy8K5md#? zG~BJKkaigKVP2J*4^#J2*;S=od|$CDJsSoN`l>st4_E7zs>g0LaD+NR)ySRRjR0Xw zcj`P+{fLXGKMEO&=xF39buVoZbOx?+NYVTBvC{Z(6a|r38EKyMZ4_EX=|$GDYMp*4 zh8&I|FNf4${dCrCFHlOWGx5N1#av(Cd^yPG)lOhI{DinuyeKXfYMTd&BA__XvhNYq zk3SPZf$DH6OMu~!@{Ia%`Nkoqvw-0RI$+j%Q5N!QcY=bw;>x7dXU0yUwLl>Tyo#e< zNuyEcMS2S1G}4DskZQ|dIgSObquIO6B*JnwGO$wu%SwJ!C=OMLmMGeUKKuD4G3uOLMxN9p( zJf03Xx~Tob7H2@guGkw(`8DVTc<2w+5Ut4e(GRyKvxXcu@c09mQYhc)=qsbYJnFUJ z)mx$A6_09=e>}=p`HQ3bShKh3H||{6W1!IbQ#2^_KA>Pnm9%00*`ZI*k2g@X1O|gy z!uI=*+TZ=L_J;uz=}!kZ?=(=b3p-ivX8NvGr{fG1Yk=VxX4JC9jfc0pTFZbr=}$!y zAlXAuIDw+WmM2rX72i9-Kv6h=f)da|uC-|=dDXXSNr>a#uDM(Y5G-K3`+I-tY z2re7cp}J#WvbAcHb|VI`d?_($H#koW44D&APP@9aj-Bc5y5u$%sq7GC2ROc-*w4;( z2=N+_CmbebLrZjN;~MJMGX3r3-d?yFLZ;;P?_0 z?EG7d8+g2NdZ$aEkUEE=0VRM!7159uj)QKb;Z*;rp=eosMyP7;K7YFlPdNnls4!_H z`o%j*=YAgg((9{A$zdcq#s6R^l;hj~4xI81nzksF?Rk3zuM)NVeO<*N5lzLhXzmzU zB@Gk0f>0^)gI@0Qi!L0t< zI3&}QqH=3$KMB(>F*LW;S_O+E*E^%a+Xh@{;>)uIVcAhpC72Q?;myp@u!zBZqcGcX zDt)iC-!jY=;JgskV%rz;vKyT^;^J5DgoE%(ysn1IOUn%ZleysElhn;sQc=`(vf4|P z+?1+JR@;_Z-c)q(2k&}hE-)U)K975nr-EirRyR|13Zbki(0OPg0PT(8x*b`2P59*Be@K`LZ`SNey}k~!7#VY;4ZYyfp0^?MnQALXH1~H9Tc94dnk1F$W%RkC zf!M4qrGZ%6x2;IArSrvCE3UTqCQ)EO2fLy(ezDW3>96VVL1HmF)uA@z&-dUM zJsljYwig?y*5M&RO?q}0=1&m)H;%R{nUl;p4@bH=Q*Ebz2X-7wG+gZ;+-%mW?-0-v z%fM*wpHe}e$7NPn?^)iQMMwVtCaeRw&4L$T2EqA~2@JwIh>Kxei#eBXc5HV;fq~#8 zeL9NMs~>B_-mB}CsR0a|p^nV^s4eLx$M%;>(7joxc0eb}h|w%m^(skDWw30Ql?b8d zU|?jS%UrA0wL;f250Rfu5%!Z$EFk i2%#8~V$48YWcha2IMm8$sP58~Qd7Bjbv$ zVjpiG^5%2Tu0>fjdV~)?*bUK@BIls(4s@kgWi(dCi29m3(t)9{{j9DOG8mb!BQr;W zzWd)7ZdK=t6IWjBE-#UZGaGeFVnUm4VlUZo;Nx^v&xU3oL``_8cI!rgbJ36LgMuTF zAGd1Xyf^7ACUt^0;D!K(qlo);vem}3c9!5}Egk;Hqi!@Ec~xoMXw_V`u2kNIX^xhC z`4t(3*G{XAg(Rm2#@-CH_i%TWm=%jfR1B|2?Xran{6bmi*vo`vL zQs{gHaT7zuu>OO!a$4!Wjt7O8<=!l4QYdWzg+ysfU*@Yr^pC?t@~V6NQkU4>It_9V zbNhYGwc*cBPvV3kujwo8+5E)5S@xCrK9LLQF(W4O@v zA0S%H$b*(SmKIi@p=`;fFj-TjqV17({Q!ZVc`)CqG>kGW8cYpsXSI>nVJnvT5Md!R5!UT2ka^~5cO_JTqhzu!esCUY+o zEjEq$pRSGr%C&?4k^Qihgqgmkoa_2SrFeP^uZDLait^E9ZUn#Q3kvQ(rz&pS+@rFv zJcqu>eO&AWmL_aXO`>U2He?9}wH2t9b>4RXmMi<3`VU)&{ z_lD7)mB=~Ni$VioG5IWvVBF?_L|Pb8&GRDvRgn8%mqDVLBA;atj4#aVs291{)RdzA ztJI#_CQtx(y^v~$mz6qwgjWTnm6mfOspx75w{{c-tyZ5?Z5u`A=@9zhC<@t(*r;~8 zIz+oYR%{H#*aPV&>ks6W32B-rg|ga%px^>jBa7{r>TEv5P{8R}$^k>|Wl-><@Y-oh zCyp)lu_q|F$sy^zSh8G$Hn;~0OHjnDx}g|ap%kyOa9;yO{W$6l3f4>Ha|pFzjk-_% z0T8+hwmAy{P*v)dp>|Xu5>LoLg~UiaSZ_8)l(gFFz|02|gt${GN{t@xB&|#r2!pQ%AEJcD+ zuN)_?x_lb^qRr!}yCxX=oBcrX?N3U{X-C@GxGz*G?Lk`{l2sW;`;gbN1@5uVo1nj1 z?u41`q5!6#N9)wBRX4^_c2U^aBT#UkXmp~TSy^KnVd^A$@^|CN{~aVf4ez)faui$| zpM+5XJev4fRor#=80sL)A@QI$>oM8O9#033qK{u4PdUdC*>=a}fIAi4uGZ4=jS%44MEbT7*70~Exo<+;E1I%x9#ht73m%~^OraDQJA8^T5uCP6`(mH?Dy^zjRw-dk z!XGFim~!)UTN#Qtb7(bKAVO zx~5Ql!7t5m5-rRycfdf+4ok6h1|8U{uB1}Tq$gX|PO3JusOUD78a9i9kkZK$ni3{) zLgyp%6B;*qwhewePeWK|L-E|Bed~oU>uzMzNs!HxOt-5Yw3&&bKYejw+|a7?_r8^+ zLf4bVdez|B6r@5k#?PjN^XL!LXN$U-UZ`c;FI9J3HCVe$DUH^u_p>QyJM6O19C3~4 zFVDR@FZ6NX5euOsFUZ>i!=qgH!o`xqX)rb?2pd{ZdW|{cwgY)PfPzQ6Z`s2u9W9Zv z%|J0+!Zh7>VpUGD#C`@0&7oN2)$RmE8RU(f=NnUaK=N$^#b0x16LbFz3LYtM{|rx^ zcwpo*14Ze%BIe=An@I!Pl;P<(Z&`t40dwgG=j{fHvdn$X!j{gbF0T_PxKfI-62|v< zgO&|5&V&gVOoH4ME9X-1PDpkN6fVf?TX^Q~x!%rW4CTI#IQ`!yP8pjZP8`+-^>!xqoW zc5H3HWX~h*E=cwf6t18sd#hc$PBBriQklEsd}pHsq!Gl#0BngYzlwirU)2L-7c-6;V#J&wekKUv(ZXa=}`r!Xk;DF)b!O znHMn$;i`ovk<)IJJu6A{>jPUq`!wmhzKx-T-M~}^zj}8fb<=MYU2DMHOQJ~5Yfkuv z3KS!!`FH3wOwEx4_P_Dkdmu1SUxHf;pHs)`gq$+ujU?KKyxJw8(1GG|`osM@&nAu( z^TKy|oF{t4+zTzhCkvqX*rJPV+%@$b19x8u(^DH4k-nu}9Rv3d3zE*DqUOoub^|H8 zPxM2igOX|b9;B0#DRdvwGEE6b#2TdF-mngwyITy()~DM z7xdz*n7)XrfRT0sC`zDiwJg{6wcFTs6ey(GNL~>|5v&V&Dr6yY7fpApOl|1nKI~19 zA154yM6%#Ax8sY+Y$XpIiz#luTBmfR&HFJ+l^;uDiD9Fy_S{G@8mV`rM~|}=-VTIx z9JQlCF(>jm0B8i$GzZWWIY|*NavW@ z%Z(Ix@gHQQJ!ZJ4SDEN0qOsYkEH_gsBV{J;nYJ zHuGGxD>Ljw`FIqUbtvW3?gl0I?6sjghs-#6iTkb4N!%53o#nDbDJ|(Ay)~Br`6=IO z2PK{qwIR1;eq^#x@Bvj$fn2f!`EmZY;;3L<$Vc&cPSRhVozLK9!eYWR$67`cfSUC^XUb- zJB3)T>^H@MlFcg@12z(EzESDFS4rqeat|`_`?tq=sPf$A8u|sTh>a<9b-|EDyRt6i zkyrX$PC%@09>&tG_UK`8k?Kab`=4@-Z?%D4!{xH`|7Uw?^IhJ>he-HXe!)K-in#vO zi&q|b9Z@CE6MT+RFy;r+XfDQW(wsqG`>41oH!90_+l2Qou3%-CcU@owyx`F(ceo>U zY;D0K79Az+1+?lPD9H~UInZx%Xp_gjSlN<0vicY`2SxKX$M7jUDDLbYwdZ~JRlI;A zI4vK=Twnr#>0iS9G=3WmIIfr0x^*j;J9{mWhRwy2cZc*4=Y>}tv#7vuGL1BRTl zNsbXCM1E)D+2}aM-GgKW%ojZIru;ZLp~vbMf(OU#?Vit`Y}6{9!wX5WObOFZ9ng3A z6Jv3%BiQPL;}nU!T9XsvT&I1HEwRSPr{O2-UOuUIpWremDZMk0b{4Qj0` zcE6dEHN~J?G0wNu4+Ia6&&%A?JbK{OZFZBK1_x_Zu31RiCF->GGBkb21F>l6jeL3|XSPSOU>Tj`W&wG*%T zoOZRiRNWw1zH>K7=BsFWioUUAL!`VLe+;&6Ytfh2yCv)Ng@?ETiUpwH+4P~i4rv>j zjN;ua5`{QR;z3HB0b-+oFr%|}@KNkRF1D&~imDl(pEY=G$YoBH#e55zEBZ=9o<4h? zXnq^sc9?rTaCI()=Ra}daqH$O3c+xx&wsMc6opTVCSY9WjQf}+XJ;7%!;~@wd9^jr z6dXW%hs~PM{c?&FT611Z^s||JRq&_?iUT8hZQGhr=Pv_~w!pYBX7-;Wy0uP|%!m~} z>NGiARonJk1`6&FIX;!AHs0A|sv&Q_O8|lyWg|}|!Q>(P zTvQDU4^Sz;fR^Vfu4gC><*DkOA&0k^oO>y{gO(RgW@#EUr4R2sz=gkmLjN6ihF)A1 z!)#%|!bBltJEm$uS!B+G>fqU8=)CQ-!{3Pssk_XTy|TnoWOt4YE#lZ?NxS}5JE)#z z(O-XKdQfoAfV1KRZ+X9=Lq`1QWDjZBucG@mIZHv;AbFd!)bSdmnstt5^1UQY^KqyL zFI2~D>UJqp>4b$Z-buh~GzFKI|A53&i(IjPzQ8(SnCi;$Ct}3m*AeRfPT4tIj5DRt zH`n+tgvq}@mVzZA#4wEU|2F2!VdL)+R~{z$-6GOIcM~-*6qO&97*rXka)rM_*kV|n zaF-Vm@^Z|};-BFN&vgV&|CHljMmYoFKcf|iZvo1XM&4Y)ac<#Nu7M$IKzrgUKAwar zCHC0SeeO<|(uR&D6Xu$}2vZ!fI!Q8pP=9+y+%W~Ze6`To?pAVs95w0uD&!a%4*1M`C?H2G4rwaj&eX31KXiTt}%D^o*A>N@HdIDOeX2DH*21 z@{$2M(SfsAfE7B6U=orkX?LJdeyh>{;|{ODH?_qze4$WDtUTSRfLlp4ZT?I0xn}43 zbjf~v8=romTVyvJ`rx<59v%vtC!%FlE3WSDID6|8VE82}CVt{8+F;eKC$tZ*YPF9+ z$)l;eUC{P!f6e8^cWD{;;}d!X3c1a+ZJ&xf$N$`(`qkDd5|qL_Djd?&p8{Wi((xI! ze}S(S@JcuH1x9YX(tXGGJzr4gm$)DFf)Zb1L%F05{fbxOf}o@7PS+MMUhwuz-uM9{ zdk75om<|VD7^mEP?kbe!fe=g)@S3W;LRn2-Q~g(1#58>)^4Rp^=9Ec=JJsVpBus@_ zstvEy+f*xFQI4gl@WEZZw-ompnVY|*1+O6pKD2%C8bf-%s|M_Eek~NqD=Ctj&=zc9 z@=}tpEHBkmTzHdAPiB=jLi6~S2u{5qifH75FL)Pvby2yC1!sJO~|i6 z6w-TO`@cZJ>U1r&q-gPb*4sfLY3bny1;xBm+jxHji8V;>8=c=dZ0Z7D^+QWwNKh2} zD4O-u2b(^3`O+6Jt>jHB(nd3^$WLE^7t62SsqKueepFDg_v)?E>sk5UhW9ctm67Qe z1zCJh+mzO0&SZ)bH_w}r?d~v2*wiXGKj~l_X*X1`(Rt)pj~ps#OOMfYJh=Tq?P2s7 z7h&Il!R!53ylk-_+mXJ)s`Fj=5Eg7D9=~`U{;HtOAJsP6^Iye|P$Ok>)9co!OK~-^ zfyuCw$Y||11^oo6ocC9$c*W7x9Pu(*NqDJ>cRLB6AdUD6L|in%@*lr8<<(aN65=zM zN@jUed@=e-J>I_YchQgoEzKZ0tvZlvm{k%ouHU*J)2(shQE`WJTCd1c?nuA}(%C z$WhNtGAKe;x6xv-+y4bhdyj1t%x+felw-Yj(t87~)FS$I;Ku%C)R?T^f32&FwLe*{ z1uI-?MHRMxW&c#$8tp-G%AaZ*qcgae@fGFX0av!xN^!%BCRBAlVBorWI%UEF@^%uV zPsm#Q8vUvg2R_=hbnzn+x@T9RPv{|2Tl9Pq~fa9*$4?_ zCCF8jYbJN<-z3Vvg7wht|ErZnZ(%vs^%0-dxCqhe6ExKQ}E%TNa1SLV`OtKp1 zYc*b~rjGMl;=3R><@|}7U~P4!QrISRrbhi5G-_otVPk^O_vSdwe}PwVzkhy;%?|jF zKR*?XWtr#YkR>ZWV+S%Y=z}=Z&x!n%=wfmcL@Kd+?V*aMDpBxv6&(Ol`w?SV30Osv zc|tAEE*mfw#PK(>I(J2$w}CR7Us{%HEA^;fwM1-}u0oyO7g7q5+QuWx8nv$A#4~W*2)b-!Oka#prw|j`gq?Zv zE|-FLnqh-jY#~jr{5QA?hlODGO!JbzSgAxwt*C*q#$MhlY(13{uti2yyf8%;Mw)l) zOozfs;R6K6#i|^y65Ae%Zo>!b8rU+UGJ(8P)ucDk1goxTD77^PgA@~umo}eIIq>NN z4=R#O+KZ-2;SJ;9v?a?@(-z-n#ftkavH!`butt~P);Mu{4-SA8gw;+isuY~i%^hot z_h`8?&CodWiqbOhP^A^6%gpNmC^-!799{oe;|(==mxE+qGmB9?(^J~sYpar!qB@Cho-}BM$0aDO7*fS}O2MWvRgsc(6VJ83Z!i1j zMmSql)sj@j6ctC$2sK5{kdidBI9g?nsV0OC;EpLQW=2V|uFIZ|tUS2$TaH&*8`v5t zn@H8Y8f%(WL{nHBU@6x6+m*(udach*1}*v}wkHmfTkvi~X+N&K(^lnY zOa5lL8X?jQ{k(xKtuRA>!3NDYoJw05wFq_uPjfJS!MEGBI(8G(DAZJHLwV0GWfq6L z4@%K>G^}cJQTl*gz4|m*IEMm#yt}QcR^|D8WQPOfcFKF|8y^+og%{x73Nd`qX)D_) zg?H#RrY^g#EA#mf^1^>`-iCG*x)tVg)Eq@_EloLycJ%pl1Zg95J{^GD6O8iPsHt6x za~d^}`P0h%A4lryV!&V@z@KkRPEhU`vbWl1Pe0%r^~aFW0vRWLXn44Mtzo=)EoJ=Q zvecJ$5LqR(-=9z|n6M_} z;W-p}c~r~le{1bN{roRdUT6~`^)onjd928zrhn1vKSu;@!Ft4Q<9(LsD<8_xD@%Bu z0yg497U|3)^0$IEgr zDAR3np__bv%7udX{x0%4BmL<@iTqsVC-af%WjYxz^Uw7>)CPt#q=HiTCZKPy>-vq4 zMn*|0)gydxRSMy1<9&72r@u~V0t_D)hzdHn(gx(^^KR1Ykm6LE2u{6SDX}d2hNUg~ zMlDxzw}mOZaiut0AU9T|>{3V%RHPJ6&sC%>JlE#nY;P;HX_YIP7Zv@hjx@BXaDLOT zBbEN^?50w`&R$9=h2o`Rv_C3|UZD4(vh#;tyXXw|ykQ9rRTe0xB0VXk=_LvLTW#c8 zYX4Oq7wupgQX4e?{d>1JRYZrs$VIH|==xuJSj-=QZ$ng7#ZkZDb`TFUv7*9SjeUMQ z$=NZX_b7x@aCF+n_(?{dhnT+|XKqtI${Q8tp$zLCZQW=l&dJgD2L;bVGA^~xI6iID zM1ev$vSb7>-1FM6>-?rnM9Yg321EWOH@ePe>^udJU1(x6d&D&FK+bdNRz1uvS5E25 zOKDsb$QxCo9D5XX7{BGnyQZFP3|MNpy^Xwh0dXzdMNj9_v`pI3n%7GTc?AzEz2wl^ zNfh?l@A0!U9`de5=8l#Vx$CrlfPxndi(Gv-zx|FPF_O68-qM2-9ARAlS&Hg#HTvNI z%AOu#KX|osa$>UjzOTTGmpv56JZKYB{7-r5v{%84W20Z!SyQ;V5DO!*{f3!0aF zm39n%b(dEYLyJ_lYyBm+x46?`&J|2AT0KlimrYX3JQG$D7d>s46lDTCT=G_d1761*Bu3n zIWT+gRq`CV{vy^Wr7o+O=0_Wtdnzb+=&fB=>-fBbw?_K--4~~F`_Y|psDnjy3iW^r zwAIPo1!=YFbgl||YIhfS!EOaR6n5QkxMd)-Kko>b*WeejIMG0uT=$2qcFJ;B`Kbu3rG}M}ZW< z_iq9zjZ-PGFh*d3nBzqSh`ytnm_HXN&c=P+h|u5 z6YWP+T{YWy=_IHks<5Mm7>f4!9Gg(6=Jp0s70|jNwP@W5A{e_1z(LzcBfz1Kq+i{ zsIu+ngJzX2i?hXI7=p?U0K;2P;>&+k|9D#cnrQ7`(&<0%{A$>R|QzoKA5qbYV*EoC?u*4zQ8${UV&1t3l`h0)DV;9Ce=&p3So^4 z(^4-VMQsVQbjPixrctJY449U|6j%*;dw_y{vQcKkz76~JYHpwyEMaDznbg#WayA$+ z6M|_v@@feb>`5nvR>>&6&*6@NVofk*GWWd_#g?Mm#{Mkin`odoD`6srSSaof-FDl6 zxfM)i?#TNF6x@xUger%rrk(3)pfGMKs@SVa($|1$Yd#tIB5H{H?qWLH z2ek!Th)llDLo=3pxVsMJ=baO>gXb9`c5f@izCXKK#~J0f2l&BUu~h_zdw57i=|A9y ze|T_6xiP$f`?boL&P$z6p=u*yvRjQd3m>%)`^Nap5;!Xb%rpz-YRy2wgF;YR#HmuB zBHnXn5Uf*S-zLcu1XV%6V?*(~^UFKCx5EoyjK9LDA&m`rJ-#*oIXL{t2)=*o=ZfKQ z^3vQ@(WVWhfTv1I>71v=PTv*3(amGLS%vKMJMmHIO46CTirx}t%Bp9v^r$G}18G*P zz^4ihUeMa_{)Q>wkQDjr1btktS2UROi1sqpe%z-L^Bq`9A>LAqZ%1ihuCnexd%V!R z^0|%&JBgF>QURtaec=)8udsH^L+tfV6uAkydIk#CRotwtS&n9Q9PINr;)CVXP8978 zo<%#;zD?k%?M$yYE#H~SZN>=X)me0{eVg>Rsy1gzf{;^Kk__xjL3&WM1O=;ULg$mA zV}ebe27h!XVOe1q2D0D(v{$F6Y zD@;R0L)mkqU!23XnE8;99V%)sTl`T_uxo;B+aJzW8V= zscwf-d0&l_Wc-#+P>|v!a`WKQJj*`ec+AtrqMc#DS9}hoSYPO>P#A6Ug$|2_(P_SS z2%{f-Um=Wa{cx`fqhLSW*9@axxYy4N6Qe2n4^0k^4S!fsY7MZw0t~m)i!=7iMjj}E z5kRg>_wIC#dGzm2kCyQqSi@Z4QIccf}92 z6SS_iMVFX5ac`aU?`C*I8pmIB!qon8Fj_FjQ6WL-8u`A2#Ib}rg+mmL4T7q@qG$)F zIAQJyr*YBbPz%jCKUy3)4u7ihY2(S~%h>zljB7aN3}ARxP-n;28|U{e!C1 zG({k<_5mogpg6JYM|P(N_g9D%?9){-lm-fwQw+_l1IoY{k@C`(K8XhwJq|SR>KH>W zm@+nof|xP|l$PN6V)Eqc>IN}m4f1S?A^+N-I1UPHP<)wIt8Cn#$wLej*JCIe6#7@7 z;K1W$`y2Dk8gI8TP?!!BUAMOM=V|vni(fWioCneY&g%&Z9!aBD1@0O<>Zvdqiuz~> z3`^Ezn6hz;x7W=LdHn}b(K^tnR0ow*r+=xnq!*Q>r+?9;tm}eXLpmBtUt@%5=X;Eo zt|)YB^=6?9OD7wM_G>saN^sjSr%5BIYhBcOW-+n*e~3&PYiYWwEb>aLI8#Q7QOTSq z*^O>{R=L3M6A^788oN1?)*-LY3!*fV_<+ai+d`5#?yGF zERsOG8bFq}3FOoe^~z^L$ZAA)`<5WK%>?t#5fS5+yr2tS>@NzBp-AM_+Kdr7Dfd=HQE|%jcun~?KD>F_UK+;G2 zi1JJ;jk6rlE`pYSpP)b|!ivRFDbJ@bqxu|fKM!je>+ytVVwhjW?g7`wxaWnjo*j-H zPA#5htWsXY6Yg#5*5ZzYHh(@VH!{k+18N|hb6#Y;IEnHwef_jjeak{Bj^UB$c6RXhBQbDRmEvPf2Vm~b)s0z zs7gEbh28ZU%w{gQWCh-9-DF@*knIXBKKfTrE-NQi!s^n zT}a0ColJ4TsM~N*GOzLv+q-%Bj~xwOQj|Oq7*_H%%dwXpEPsFjP=4pNXfkCXuQme| z93(%AeQ?>C*6{)mzc7Ygc27=fiprmuOm&NGg#GjiI4 zW)ylX6uNj8MT0`MbrwaoLGXazN+=O1pVa@mc4f>FG$1=S)s>h8s-?q@B(xCP7i%kZ z=}j|q^?Zk3{Kyf0KroSlnnS39c|e%K%{4k@MY=s*V{MG1UvQw*;pUpUrk`hv9ku%$ zGH)SMSeZbfihFaYYYXV<-5hZ&tJXbfy~O7^t4<`MJ#OdUyFdAU*&O zT7Vbjw<215Q1DE4f4aBvwkgN<33&yN0l$=k0=3mZQ3@32eqQZFySpp`1#jWU)T7~C za%+k5LqWk&QQ6ZCd-b_ofp-~5K}tVh*x-_i*qxob!S0>J9p%T*rC8)uCC;U!mYQHx zymV_EO!KWJCYBS@NE?ZkY1|4o^XF4SD-^PRzR2pqp+1%aKh)v{ZfW*(7#J=-CMLqn z+t!?Sk)gx#M+RB*>0GN{%#D8SX=;{4&9PrV-!)0>DZws2HZzAjl6wkVZAyP}puG2N zk6otI_w(!pyoFUrX|I92=OF)97u?`PqGu>Yb&bfR4Fyoos~vx($P$ z2?qtU+s4o;8fk)P7gkiLt;XH-BYp;sgRRIE3T&&Xq@9o=I@jNAHzkF}x5exuWHGG* zWnjm}V(0vwQpP+01^bSyre<{6G@j=R?EWxudWTQx+o3jPQj5X@>I>oD0x(Qwle}a;&bcS_nWuIaLFkXnuRKNc=o2AlXiD)RlSF-#P z3T%%mAZ_1XNQGOv}}w5`ooA-EO>0sl17l|S`)+pmV-Nj)$KMYtV-!c*FzE_-5)z|8dD3p-IX0i_NCmzk=*KV)(aSDUK#(2T=%` zQ$G;*+ypkw+_u*Fbc4Us!4JA|bH)P0N;>)KWn9pSQ@qH_EFsyD3Y5|jYl!*$(f}{F z!!vevAcszHA-}(-lSjr0D=DHADvV!1s1lDfX(grcy?plny(ns18o|*EoIqI`&#_>8 zu@i;}?HY3K4BLOaMhr^xYR6ppcn$UGteLEwMz1^L;Kwx?RJ02i$jjJR1l^iJL3pJ9 z{YZj?8KN4qkaB=A#^cgp%gS@{dy80H6wDwegJyEx51?QpzcSW(v*yn#g$xwM@RN+Z zHk9}(J*)oEtg;4-%UZgQyxIUzuw&gAACFi0FBcdn+O8$@uAqnl1y5fZPE<6mvbz0T z1I3iJRJSWMyBrkepg6E{>#>IGw>1xxkkIW zHWrD6j*SM6*hrS$uv9T@BL#JXvyczRsI`e=yFp_nn<)u+qnn$Rw0R$9-F3Fmbr71L z+9C#w;*YyoIWUL+Kf*&T7W-VU|i&2~5M(x3ztDGQXux8>kc+@zsSzH{Bf4*76p!7>Ryvzj4H_f? z!#kGHoqqexF4Tt?Aax44Gn}=j&T-JqzjnApQ0Vjdi4W;ypYQhKuIwvURvc-!&I~(_ z<*q3G=m-D&MkuC!dZ^p(Pwk-Vqn4&E(EY%L*>~_h+?XY?Yn{@Mn(e;op+3HT~ ziu+gUFJe4_i3Wzp)B~ze&qj2nhsM+ozA+9@x#tE4_OV(~{8W319%ak~hTo=6EdQWc z`1cuDTNTPc9`tFmo@m}=P?P}0oAs)<)!r?y%JK;zhhi--EZ^klA5ShU|68b&aQ?4i z*KP{x$7^Lh(aYd=6T!&#h{VYEdG+Kjg(q3?db$n{jxu{{+>9>mR?_R9nkLHP6cDbl zH^J`7HgsRLHvT1S5TGdX1J!I>7{+5hce$!?!91t zrT2>^s5ewOGRGom56gxC0}8FYpVIokGx_hQI8f@Ff|A{I@~bv&Hom@x08pmPYhISh zO5FMICzYaFzn>oT!emaqmwC%lG(I4zTDj-@$gW4@@Qt+0TiR%EgpXq=zPF|{zbVb` z4YA-j{re)7S;{-|`70WUrc}9uVr7$dG+D0pIr2MHFGTIeE9M-l*ZElAN*82x*3oAzaOO| z*8;)#3n-gf_l4ca44*@M_Ps01W1+V3CK#`*m2beXFvIlU!sDhjxNgA68Z9`l%>Dn; zF(vcyK(n%ys_<(;nPkYO=cy+jZoQel^rC=)q2N>Z zgA|LrMgfPFG-iOtlGi<8Uz?a;W8`9=qy3anWZS`YV~|T)i)arF`<-sRwWA*nOXbBe z$!X)Xuph{q&$|=0O+J80nwZHY+SsFL!QBLQRqea@QVp@?{v`=z!@U9$SsBB9c^sx~ z1F+w@)+RAm!W$=uS zDg;lFQP}3LrAbkm%IfrEO5rnpiVOXJ&3y-0R7dys?n>LcmyXm$5gQheCZZx@Z>ZQ} zSK6W|0->nbz}^)soY)n6M~!aOs90j`VvLCuHAamcV~NI+?>D!&ae-*w_y0c6htK2P zd(WAfGiT16Idf*_jCvq>CaYUkG2HYgj%QZk?IWK&zJ;(>FrpEY828uO(Trs13+eJW z79G9-@7gC&@yzXRbgv)Gi&F3P3t$O{ehJw%4l6${$6_nZPF0)#ucE)R$u_h1|84Yl z?LS3-r!6U9iWKYJd3k>^kicup=}qms&cc3sfqj9LH4-7-3+Gx`{L3)#%hRFMuFgZi z{ZeAVOMT_@W5G*t&z=7J2<9I`0{^EQS+)69#d*cJ%|&)|GoR?uy|8x6G={XbHRu*F zm9tuOAyTn-2`$&;oREjU|$fxMgyVSE`OE}cD$?bYpCNC7)_hCS45;Tw@w^E2Ru zE}5&K=+ME|BUh$A&09we-L~K+b`CKGF-e*NhA6C5-_qcDj*M{6zQ^%3&78;m@g1xZ4 zP)Rv=)J~cE3&r7N!QrSU+1+Oy6-pJ{qGteMd{};NPx3jdL}no}df?Rh1rRnFJF;b1 zQ#)nzkt!t%b&W%hA73qxo8@hkuYqe1+;->0aZEJ1GUi~&6f!B9G5v)Y0@ zT{tH*v|aCW7!J3nooL=>4BZGe9=-)UFS(G4YaPHHD%h^p(iWfw=}-g{rQzE6!kbh2 z!KsW1kD%AezNSmQXetqDnaw^eWzfF9ZSOHQNTaGWou*$!|amSo*(%A($PNVAr z4W2GmMh2)p3##46BPUkjj;{~z{MICTAy63G(0g-%(rK&OsT}x6%kYDWFXM(z+b!yf z+9%7z`R$_e`unaEzs2lB5M?wzRxNzBoHI4w0d9zAD#FXDeo)fZ9hl2w9p3kAwX=Jv zJJf_y=K}TJ4s{Lg>JOr9NJ%JbEGks;@SGryD3=jwy}%_n7Ga`*^_bmw+y%|*@a0UM^T%e zjDiPAx><=5MuDJimql4wdGf1_WrKc*2fu^iAchS0qZPnbiixBNu>2-{6d&X{8jN{x zMM;fEtDUsQH_~Hi6jgv(t+;xXe6!Tf{>4{CkJ9GV!L8<>sRz{~iKoKin&>C$vTOJ^ z&#QYhVd{!`S}Dr|gX6_awWrn-C`_y--WZb^>9XxSOAH??st4!rTg>-e%DgU`p7ytJ z6FtxlbUjOLN)fS$*`1#Sff|2}62_sr*QnP7wRd}=w`@gGHYlpzK6CwEZO8Ay8@84O zRvrL^^^!)DIv(5hRXC;%fCv-Ul6MMw+U1VN+K{~bVo^k%H`=)GQS0>;GxQt5-E zt9DZQ(U`I7AkvOgyJ}-nQ@w``j7t0FN~i8Wz1|fD8YCMEC$0*SBvW@o9P;|M>uedf z)^0d3Sm;o2F$ECD-ww?x*8Z->U3ZC#@X?P0#14>KF`P2*XC+%aWjmM98|O)o;D%-^ z&*>A732B5nuK~mvX_EV_zED3W!9#-7hbAp`K?@NI>MF`egftazQQ<7uZT`0?Ycif4 z@LYkX?iSfh#B1s z;IrGWbOXt8c=2gA#H|kbO;*>c(%@I65ZB7s)o7!XA#!#frK>DM%T6a2|F4UaYAvNB zn;B|5wOp7;g7A%#zq#JYUG$+=pGKFNdgl&#to)Ep;>Ksxsx%TL4yj7b^D{X7<}MXa zh1p~Ko2XfNP8X^jSP>o#*0mw73YltI6yy@c%F6|7t04m>h~NIP`gO+To^WHx8HD~u z1=G~dZeIX{S!FAiRvf-;$#QfF8RMho{S2Z%uB^cL5A6F?DW7hpQibj_s49P-w#<;1 zcwJOc_v!5n$bQOwTD~4af1iA3;m#GV5*)lHQhykS*`=RB_RtKeZ9OI(pR{sxG zN0Z0YZw_koxiyq)CTmDZEL5EylSeM{kvh%C9R3fhDp&2_cC$rKXw-7hBG*lp3aO56 zWg8`u`ZpdWjKxf5f-DYep3wd!e-n#?Pbiy>GBQ)7VMHaV9(hobdH25%51GV|I+3KZ zE6U?Q8IniWu>eOM$E!_0h=sA#$yJbRDsw<)wJ`^cI;8P9E4A1^w9LPifzOp^xgs** zMuf^uR+hN`rv`#do-tn~GPK`46{iK?dOvFtwB+y<(dfo(Od&UHsY3G^+FmYMmR(~C zWK||J=09v?(tEs6gBuAz{8L<>9OU98qA7MHH|E* z|5u`5%sM%Lbi!-Mo`BCXTzkk*da|Px^fsuFaITf%Jh`M%}r4YRy;SO#uM1Rq! zoP)j03q0Vqv3x;!tJPhV?Wooo82F=JQREu6bNiXE#Hma3?%HaZUs<8ewPSGQYdbo%V;css778DeqTy>AvsPWxv;#1_h5a_F z4zKChIxz0zJX9hku8??5f$Ol#Mo0kyB{a%fr?xBgVNP%-ysfTQOpe0x(RpB&A)3E_7xcX7Cbi zpR7MNNx!1t^?$3C?^}Aj9&1gd_(BAK9>S-;73T!ss&jSW3F>B`1XBV=iCG&8*#J4| z@RpLEZ_L?>YCDs%^wOZpQ6}XSpNqlr(#zx<d^byd6O~ZGRt5cNWcgYt z$d&D=hx@3x2`z?CTU+)*vgq~phmwlIjGit>zXPjEj=ao2?>za zU{4Yf*&@5?UFMHX7&MymQhG42B33uD0AbGNkq-x)o?`dF)9b^w6kXy_@0cO6ine2| zuT1&%U6B_pJAi#FC{OBDX{p|cNwGS`gdRb0w#SP40Rk0KADU_^GG%gj)jsK3RD+}p zK$!mrHJ&F?-{|t8KNj1p7%xySY;`o=A^`~YEHe`GkcRCg852A@Zg1u$?NLw3sqDu# z#|X^m*l>2=>?N_|JR>8Awj}SGsE6@{k{PpbupBnu4U{tLM~Cs$=+y^ zP({)2>t~L?=Wgz4wgEL_1u#%Te6&LQ&^cG_{)>yJ*&Z1(JU%HEzk~DNWq*Hs!5^M< z@E~UJo^%BdHxvueVGU+#p%NT+TYH}0AN=EYt31j2klMuz2w)9GY*0IN6p`81F}4+u z?bXPBxqA5?_j2Pr1rIfdS2!UXCQKCzJH-VI>*t6x!cv#7B1M;~ON>=0zFd0aQj5FW zB0VVw1%Qx^cvJ%(7|jlL8gENeiM!y?n(_t2@7$@vVGNh<6)5=f@D=6jb0)vtOW0Vd#vT{u0qKlod1(wMn)I|m$OhRj)xbSdup?wQxYl)Y7V^NOX z6lr|dqjrvms9XPjr6Gnh)H-T5ySmVsoej)7rY>I}oai=Nl(j*<>-5@p-lU3y<*EVuvW_9K~2vGA!*FUgUOk&$VOo@5~NnWC)IUwnb8V92n1m!MdKMuOYJ&>fcljHdK59WVzdjYRq)|o3S~)W7vil$sDR_Lq0c=LK5+ydcb7b`f{eN z>SbQfWdf$4I9db3mW|rSxHnoCnR5{kwkQX|>T5$Y*!|p&wzTFXHY%^NrL12tX|Hw) zXBP_WsLv??Phn%mDJ;O2a>1OmEfcY>^7nw)V-6w-BUWCy-DTp=RDlHH5c(wtDncI4 zWns{U(`s#I8R0Xq0^si`wsecpb3<1J#e;D;c*hi}!dU$pAfZUZl)1%Erq=^ojUQ-$ zw38sUas#@|j&#bGAeqRn>_lagP_MLY{5+{a>zXp0Q8^GQJ*s^5JB#~aLz@hL0G305Rzwi2r6#*$2_5mLZ-MwMA*r({(Zwcv+@)Wdt@pk zEkK}5q}ARA28#}|_tGPaD}ShbH!7!_)MbKafH5lX=Zbui`$Ak|TR4f787H&*^q z&`u(Sj;<=Br%NeZs0lA5iUD+ju57YVv@jo*$fmXzpnGK6^H|VYDX32=AueRHCE{4J$h)8ieW>v22MHJG zEV^W%lhK{em}uM!ZIs_vR0%s|@l1NtB+5SgW`g`MjXjF;!pJd{v zm|Pb8RAmY&GBk!9qEhCl#A)rQDk|}S$=aFk`gUJ!FLn|(|Na|+p%+VSMW!iPCpF4b z)vPM@`I{U_MQh9EDl$051YF`Sy}tr^{9F0|C}m|aKuZ4^()VdzL|3PWkWcO4>f!`1 zDPqXVF++O1V4hyV68gX`U^e&}>vK{F`g_%B3SL!%yy)AXk-=~;F@rXRT6Le1KaGLa z%WNqyagrAm14A{}i@1wOVO%Y#IzeS~v22l-_?lT#;^mHL3=uRE+x3NEOeF+gRqdU2Ww zMon*k5=$TY_9^;ebsv?m!}ZOQhSj|SLd88Yg6FZB4;2F=C^ZjokcEpv+E3I47;XG1lGV1mzo-e9xBaPYr6nPch1xP# zx?-6>jd=k|D+kaPVAd@gGcZ7OjdW-=>A6z{tIh&H1btkO-aJ>kXj=lo29=DPlyl$h z#lCbPNLwy3?eaw-K_^Cu-hfrzR&7{tvKP+opeoGmqWA)7nZv|!UQa%Ca@r|C;E`dK zln5*>zMq64#Rybcbfv;+xCo;^@?txJn~a@0^m+31!$K*-rnZ-ra$jP*g*aSj$JZ6b zYJZ~(f%Cm6c9-Hu7MidhJ%9Uj|q{1VIfm8iNFWZ``<0s^McO1cDqSa&PB@`==hP7Uw-7`|pJDbBY6hzdIicexMZ)c{F zq-nV82B5NDHNV$|-0)`6_XI)^x<#R&gr~r3-GHV%#fba_R?2T+z44%9FfE5P%)JMM zsSQ=aotaDX=h=4rdULb^0dvB@dusoG7>LWx+<1sKSkeDFjVWpi`@-W_Fc}&?^fsPo zur17}M*}85z!N6-4E7x)`#eUaQo$woRkEB0KHB`Q(_wH)YAwZ*5L$j87$t>5k`~Oj zsF;M5bJ#ST(K@C9+ai zG@|{ek@gVq*<5n(`qSl`w>k^k%aAv{M;)2XCGbQ_q3RS$VGr@0br8k_3YA;}Tld(s z4;cujo?rq)S!@cWS-=e14-7UgxE-4|()Ul#eS+Ab7_5E*!WJSI#$}veU|q0YN((8w zhiIxutNjxgOw9j!c4yj@7tJ0C7{bm>O=Gfo#5!bSQJt+l-l5(SMeYtm`92aPeBrbP zVUaIIADy7ALK{;*q=kkV_DCI|qEOm|SCy3Ihbl`|WPOe`RVh~U@}?iN`*Q{7^BeB- z%a$f$;>~2BR#z9?*H$){Kc2-Amgw?_AW6#fL75M^ZHNu8JBY(f7E>WcruUm}YQ z=wN=6GHWdB42fR)H6PLUB=%^BK*O@bNq@z!tJM3~+x`Y+BzOL)4QXj{sU9_1ZIvxM zWkclh{|_)qC|)CV^+7Zwk@CCQR>fHx8*y%E4%-gO;2d`P*>iX{HtDL3bn74&~qwr|fnZXg67L zTKdHbXG?JO#+6=La)H#(igQ&V&$sxjI?|4^Ejee$vnZWeNI45ybnW@1BBH5)?7C!= zs&=U)tgskcwl>O|AdUS=rW}21hnYo0xHu!TnA*(z`pUdU$cI^c;L?dv^t&AwuIe35 zVfLKAYH&EE;aS>|9=6C1aW5mv8q5nRese`EOqAlU7v{|h#qe;laX>*Z0>hVJ4GM|+ zIe-(c_6UJtu^$)S2;sfUvwJv_+lyqM`E_6NSF>JR2iek+#nbjQV?SJqLmjw*(v^YE z+EOTEvF1=qIp7$! zwUfC}zHxBq@V)97}-duGpJEt`9dcRKtlG6E>Y|wi!UFKpnWYvk$^a^G<$_dBW9gME6cD0D z)^-D{O3@nF0>E1KZ5BlWL-pwxk%$Gk#SMANdPa%?sVzGOq;$AYB}bR+cA1K!)y^G_ zE5|kfK-u{F1Yam8&dJqDAxH0bTDTVwI0o33m{KDCNIFcMKvS+3W58!c{lU((4(_P( zs_0xif_6oVhWj_Q(#G*yJ^C9~laB-90RgMkYSuh$yCAk$gHadbX@Hc4Jb7;yBdWlc zZ5pg=db;5ZLl$4e&|(zg_M3#U@JZI;r73#_`yCnrecJ9+uIwzG!$vfUBliX6E=|esCUkuC@VUtrJZ`F1PNK zQQoobg{?sWJ`PZa#y!-F-c{g)tw=0L50qAAq-Fba7JQQxaWde*b5C*7gbrFC5Vpd) zuJ*WTU7ovD14J71YGANoX{@`>@@lSI!P__UZsegRhNjf*-`H6EAAf2{&a)B->NeKz*$qGe8!j;z6tD9g-#q1MkMY`$9xw#s5 zBnPEioTw`3ttaQ&+y`&0k;Z(Jo_Z|!g1-=54I7N%#)?7<;ji=ErqgV51`aojY7LNv z`G%cxYiC^B{O&$gtISogNlhORm4YO@PsjXt!1UM_+?&Hy5#p#EUTERxJ{^hNkZ7-i zqg~(G3lQrXWC}LWnm85NF0yc7>nn$V#R7tKTKM>J*z$Al4Olk;VawrWX+Oj!oOz}) zK>m`_?5Jwe*Y~%H_6A6W0b*{!#Undf*-SoXfHVSxE%i*F{cf?ovh95aX(W=wF?eXMkKwq=$^M zrxM1M@o_EvaxWh=V3^|8)2hhbB}vRZ+VQR0x}w8N1BSN*ncMJTNf zRLn3A0fWtg&#ka=KjL+-qX9#_MA^YC5Y01w9)(r|Ei@ltoPk}2%L&n;5fv?y$dMEEM&L(F^)l!`inuX7pgTxRlc%K$zGL33q@0M~xA;43IGyw4dcQH-j#| zg7;~42Jx@4Q*b9zFc<%+-1JV5PL5lL6l??v2vqc;PU*NHw%coUJ=N8>u9qclOJT&+&qZuzJUy}+zbC2cxxe$n3ulnIl0X`Eb!OPnYRED5yS+!8yqb0L$gr&MrQRP23a z64n@GhLzP;vS5@^W0CrQXb73{AJfg`XqWc)7%@732Immfob_Bh7ye=J!a`iI0g;><8{Q%)3n zbh*dP6037|&6x{QAXg!ktKAC>HZX4P@YMTf&o9_yOwu1RWl~LO4Kh^8rcdpLg19 zJFgvc1@CO2&bhLCp?2}qOou!8z`DbujTUxa4!Qm2HQw38;%?^?-w(Q?9;Qo8#*{** zIMp-+y3@&M5i_V@Qx~F-AWr5Zxh?*%0Rrc{8kU{Jw2{f6YWqa z5cc;*(Wd5`+{?OKxWtp?U){B5SjH32m~KOLDKWYPg(6j#zIA4mD!+V;5Yvq!y*S^@ zuD!&tP!oddo>C6CdT)F>=_25)Uh{Xk=Q~c$uX;;*&)B`otJA6Ci;wZn76&>s?S3ZZ zQ2p8OBzUm8eqP?j7CsiZX^g@1uMM+r(%kL16W-Z;Ho-I7am>NNo${*U15?X1xnT9N zZGLC!;hkA&8-94abaVfj1?}<9EYnl>t{)mXVD5=%yff3HYh2ep+=Q}kFHKleJ9Yj@yff}K>QptZ*VL=qr{JAg ztS=ti-_W-1IOn1fA{$m#m~_M=^+(TTfD2T)Jez#ry`p7WRD8m)_{7*&y??0oxYoAJ z>M1Y>S@Y5RfTjug?d*(a>&EcZ^q7<=h{LY$k~NFkmrq$GMI^h@ZRD!%8$DdM;+@T9 z$2xVh`pY4(XfNKGq5EC^{#_amZSZqiswfWyZyZ*q%%emv&a*_@8lNv~0{<+dF@NLJ zD96M;H`7wtStA?)h*De34(W2XB>dx(YoCyf%>Pz6{!7`=mK2WPOhhd)WG3CO&DC+k z5wO%csp-j4(cpbpiY__n!yB#h<~+S(k`m+M2Y9DtBZ z_??ylCr>InTK3^3IVxsw)Bs&-9qBCENAEa*MxH+0Cgxua^hp~qA}u{7Itk~}64Qr! z2L+06u~8`yt`DzF(2|CwlClox%1N-Olp$2P4%dgqg>#i@m@ijT{B^Gm=OzAHQHQJc z0cuHwzMRts6rLLUa*ph^3V9sSnCWf&xT+@l#&x;5rVhT0tgy7G#MCs@q|TS=x|9sc z_u@UsrXly5y3E#i(Eer|j{ws+6<41chHyQpQX?!qHf+pkNY#jIpzjpQ4KvZtYR27D z(v8-f4c%zM+32sd;QE+Qquv^Gy-Pdpj=3_BwoTwXsjxL~Len_Dy1soEPGw>;EIwIB z&AW5nG_^Zt&w;ZbB{9uAjUu-36)8EL49J6hwZf83v|!l~=A`XqiFk+V__O{bF+x!QDeB4=wJl^8!HDlMKOTX7ZW#$>KL zMN9&ZZZ_mAvsaEPPv%@LbQur}jA|6s3M;o)CvmMPdNS9Tnoi+{(e=q-^~_0JExNsp zcN0^sn9Q{yk4c;_?Vf_==7jh(Z(V|Jh%PZL48Q9L{>b>DI(#IXbgmLLoW@}j)>H_> zg{hp57EgsF1usHw=2N-(ls}zw(hr%=6)P#O5x14H=4wp!qi1p{DvJDyv(k^s=6aY> zNFyvXQwzwEbrp>nxrK5;^kXikQ~K%~5I5U|JO^@adjD12#&YV^n1Q<3^aNds{@Pk@ zlq2Qr=XT|;M$@-np>ZUS?f7-@05_6)?dR%I!vZdi&i3I<$?+h_+**KD@8TS=yxT!8 zf)c;sc*;D0Vs93pzO4>`wZ3gMmSnSsv!?6~NcLa{XRp6>kQ;AC$BuEKlzof~ras5G zK)u;eSzQ9@PZ=L5dl$4swdFV%7;Kr-i&r0NRk*lQirKT6LPTu*X#*ONH zrSZ}Kc@el4uny`BO4X&3-z7B7!i!jg1iBerzs{NI7hUE8Oz5enraF6v5L`vu^4GaI znp>6k(9gNf{ivp)k2rfec@L>)J;0jWzI&)y#y!;H?mg~2`&EJbA8?1M&Ld7ur|+S* z$L@12DET&LOV`dq1}i?~0!)RjN>AT&>Gcz?0X04i?#=vzt4`lJXd37r|G~X6 zF%4k-gZjV2wt@n!rn0`;JFY!1AW-9S8VkCv)>zS$KRE~CRgc%!8tSgq_)x@i$g=Mr zV9fOL8eacpd5r^4x2-kx_4_MnP6$6eDbkv^&~NwAoDI_drPI{(<`{F+Qd4y)L#a{_ zUxB_2fgtvot9hu*oe2>+5W)rPThG@tP}1a88V`M5uBIGM-P&?h1zECOsri!%@=*H5 zw%h=EeH6@hTBV8BuU)0NZ$)eEdBpM_ihl0gN8>`_*-$^lU!m1YH8Dv{2HZ2;{v|r|BbpoLQtfYNF4+rI}(zh4(b(WOYr07_#>@l~w-r^^NXp zx~Y_b`neA^7nRgn!}I!hH9ta0X>*_yn2si1!;hjpPc)WLo5!AL*3*Lq9IlEl*3?q^ zlkF<5lKxV$#!N*w`t!CFdrMQBuK$jHbubPP$7h;pDvI#u&FRWrjWxY`p{Yd6Uuc@r z=$9JHwbfo~aCd;7uf&p=Q-?n^RcT&2m{jyT8aUCHcSaF6m!nq*@T-4l$_x3D-78IR zWiTba(p2HYgi*4NE;TJhr_Xt%v4HB|%3IK1E4cvug4deOO3Hf)rU_Xjz0ugA9)6+d zba`(yY864B_)-1$K!8rENxMU!h@fa1(#3bE%JjY(C;h1R5Q6#`D|OLOW`kK&#^|Ks z-l+qlFg^6i)so&iiKq8xvNx+&2hcx_3R<#=~$n#OtR z&nfv3B^@>8Cy_}AU&B`DJmUQ#K0|aV19apc!dIcuA-og0`|+K7hD9YLB*kEqjgJ}Z zosg0q8y}mIls-fkm6#BpAQ~G<(Svj`Y2E{IM~O`Rpj7!+LQ+($WR%FC643;@SpBxT z{8^<){lpNyHucZhUyxfPK8GUAAv9M)`1(}Gg6F848DA0MGtI@PGf@s8-%1$bGnYX>R3r8D@fd`sYWV7=f*nQ2G<-`tvAI)pLy|Dg!GcRlpiUU9^-wZv zR8*5kgYD<}!17^3F$O0jM0x9m3l?0R!I7a9r4uo{oBnD&ev6Xg+knHtZ8>*3o`3^~ zBinMm`f2s~d?o$Zl$$^vhcSYjZHl3`K|@G_@N11dUynRGqIq%}@+;_lE54#SIz3S| zsP*ox`QmcaKAdk%mf?ID%?Ri3(-)!qHu|G2tc0QM`GF>s+5{^1eJ6ez4+}Kj8+LdU zjB%RM8EtoVJW8w3giqI-bm8N19vycAqCFmW<6T5a!!HXOl)zh+!*S`qeMfb`_wldns@-SG>r6)CPe z`1+;?lx@W^yggOwj&CkK(8Qa&Lr~r%^A2=S$2w3CzB%0(1fIP4f*(wiyYtoP8T+qc z5Bygg2>|dsD4M-m>ChbSd-7g%t0#Ysj`rjI#3Bn)c}G3hn?ItY;!UVzg}%ruA_c^N zA{&-&2xa%@J5u{rkcsTRd^7b5iM^9i1w)5sPwFn8F8$WhW=0k$VkA4~YB{zs!a=+%zt)`H^}tbkYC%C7-M$ zWNl3clORb39HG8KnZ@b6iJlwD4;GrknjloI7)jRp38VR3Xfq~HGJC8jIu-KtxIYw0 z=R^o{-E`hxKYJmJ0oM1;==E|wTp6ryxdOG-uWyM~)JNp;E0p?^OZWpS+O-67bMJdt z&J`wToTyoI43BFU@zyFjyqx!?(>-7|x~}5)LO1&AFRtRZTF_E|sEHEjXLNHDQGYC^vORD%plSuewqEFn)E6eFOZ|8%-mPK4+$tKQO!a3@F7jsZn zZ&1%a_1BZMF=XVGZAGR-j@Vm%->>vG2NnFqs9 z{^mgM@?bB?uU7Q!VoVLAR(?c1s%lPPhbiq{i8%)wPj`LI*W)>*+ z*vrtyb*J-tt*BEKSPsPt_+>P54wJcWd2>EJv5t2grq$uBCFLAgg=ivI4{XGO*fNI_ z`y19VrCn-7RI_z6;l{#W@l2|Dkgwz`#~_j;4#_*E<%`4QxIJ=|9=frgch&^Y5_9=& zHcxI|XfgK#{PejVV|*#Og_-bQHv8ozymHRjBKE*+R%U23<1*FBn$E9=B{VLpOqr&9 zDIZ0SRO+>ZeC77=G7GM`6L>OL>22v9UP{5Ihz%10T%{D@lkqNiIPqjIygkyh5uUDe JZky@c{|8zKnQQ<6 diff --git a/package.json b/package.json index 54e6c2453..3104f922b 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "bitlauncher monorepo", "author": "bitcash.org", "license": "MIT", + "type": "module", "private": true, "scripts": { "build": "turbo build --no-daemon", diff --git a/packages/alchemy/.gitignore b/packages/alchemy/.gitignore new file mode 100644 index 000000000..9b1ee42e8 --- /dev/null +++ b/packages/alchemy/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/packages/alchemy/README.md b/packages/alchemy/README.md new file mode 100644 index 000000000..a6afb1292 --- /dev/null +++ b/packages/alchemy/README.md @@ -0,0 +1,38 @@ +# Alchemy Hooks + +This project sets up Alchemy webhooks to listen for blockchain events. +These hooks invoke Trigger.dev jobs that process the events using Viem. + +## Getting Started + +This project follows the [Alchemy SDK Developer Challenge Guide](https://docs.alchemy.com/docs/sdk-developer-challenge-guide-7) to set up and configure Alchemy webhooks. The guide provides step-by-step instructions. For detailed implementation steps and best practices, refer to the guide above. + + +## Dependencies + + [Alchemy SDK](https://www.npmjs.com/package/alchemy-sdk): Used for interacting with Alchemy's API and setting up webhooks. + [Trigger.dev](https://www.npmjs.com/package/@trigger.dev/sdk): Used for creating and managing serverless functions and workflows. + [Viem](https://www.npmjs.com/package/viem): Ethereum JavaScript library for interacting with the Ethereum blockchain. + +## Documentation + +For more information on how to use Alchemy's services and set up webhooks, refer to the [Alchemy Documentation](https://docs.alchemy.com/). + +## Related Projects + +For details on how these webhooks are processed and used in our Trigger.dev jobs, please see the [Trigger App README](../trigger/README.md). + + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run src/index.ts +``` + +This project was created using `bun init` in bun v1.1.24. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/packages/alchemy/package.json b/packages/alchemy/package.json new file mode 100644 index 000000000..3fafada28 --- /dev/null +++ b/packages/alchemy/package.json @@ -0,0 +1,20 @@ +{ + "name": "@repo/alchemy", + "module": "src/index.ts", + "types": "src/index.d.ts", + "scripts": { + "create": "bun src/create.ts" + }, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@repo/tokens": "workspace:*", + "alchemy-sdk": "^3.4.1", + "viem": "^2.20.0", + "zod": "^3.23.8" + } +} diff --git a/packages/alchemy/src/config.ts b/packages/alchemy/src/config.ts new file mode 100644 index 000000000..d0806a706 --- /dev/null +++ b/packages/alchemy/src/config.ts @@ -0,0 +1,22 @@ +import { isAddress } from 'viem' +import { z } from 'zod' + +const envSchema = z.object({ + ALCHEMY_NOTIFY_TOKEN: z.string().min(1, 'Alchemy notify token is required'), + ALCHEMY_ACTIVITY_WEBHOOK_URL: z.string().url('Invalid webhook URL'), + PRESALE_ADDRESS: z.string().refine(isAddress, 'Invalid Presale address'), +}) + +const parsedEnv = envSchema.safeParse(process.env) +if (!parsedEnv.success) { + console.error( + `Environment validation failed: ${JSON.stringify(parsedEnv.error.format())}`, + ) + process.exit(1) +} + +export const appConfig = { + alchemyNotifyToken: parsedEnv.data.ALCHEMY_NOTIFY_TOKEN, + alchemyActivityWebhookUrl: parsedEnv.data.ALCHEMY_ACTIVITY_WEBHOOK_URL, + presaleAddress: parsedEnv.data.PRESALE_ADDRESS, +} diff --git a/packages/alchemy/src/create.ts b/packages/alchemy/src/create.ts new file mode 100644 index 000000000..9527522be --- /dev/null +++ b/packages/alchemy/src/create.ts @@ -0,0 +1,30 @@ +import { Alchemy, Network, WebhookType } from 'alchemy-sdk' +import { appConfig } from './config' + +async function createAddressActivityNotification() { + try { + const settings = { + authToken: appConfig.alchemyNotifyToken, + network: Network.MATIC_MAINNET, // Replace with your network. + } + + const alchemy = new Alchemy(settings) + const addressActivityWebhook = await alchemy.notify.createWebhook( + appConfig.alchemyActivityWebhookUrl, + WebhookType.ADDRESS_ACTIVITY, + { + addresses: [appConfig.presaleAddress], + network: Network.MATIC_MAINNET, + }, + ) + console.log('Address Activity Webhook Details:') + console.log(JSON.stringify(addressActivityWebhook, null, 2)) + console.log( + 'Alchemy Notify address activity notification created, go to https://dashboard.alchemy.com/notify to see details of your custom hook.', + ) + } catch (error) { + console.error('Failed to create address activity notification:', error) + } +} + +createAddressActivityNotification() diff --git a/packages/alchemy/src/index.ts b/packages/alchemy/src/index.ts new file mode 100644 index 000000000..c9f6f047d --- /dev/null +++ b/packages/alchemy/src/index.ts @@ -0,0 +1 @@ +export * from './types' diff --git a/packages/alchemy/src/types.ts b/packages/alchemy/src/types.ts new file mode 100644 index 000000000..457b6c6b0 --- /dev/null +++ b/packages/alchemy/src/types.ts @@ -0,0 +1,46 @@ +import type { Network } from 'alchemy-sdk' +export interface AlchemyWebhookEvent { + webhookId: string + id: string + createdAt: Date + type: AlchemyWebhookType + event: Record +} + +export type AlchemyWebhookType = + | 'MINED_TRANSACTION' + | 'DROPPED_TRANSACTION' + | 'ADDRESS_ACTIVITY' + +export interface AlchemyActivity { + fromAddress: string + toAddress: string + blockNum: string + hash: string + value: number + asset: string + category: string + rawContract: { + rawValue: string + address: string + decimals: number + } + log?: { + address: string + topics: string[] + data: string + blockNumber: string + transactionHash: string + transactionIndex: string + blockHash: string + logIndex: string + removed: boolean + } +} + +export interface AlchemyActivityEvent { + network: AlchemyNetwork + activity: AlchemyActivity[] +} + +export type AlchemyNetwork = keyof typeof Network diff --git a/packages/alchemy/tsconfig.json b/packages/alchemy/tsconfig.json new file mode 100644 index 000000000..6432ecae3 --- /dev/null +++ b/packages/alchemy/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../packages/tsconfig/node16.json", + "include": ["./src/**/*", "src/lib/utils.ts"], + "exclude": ["node_modules"], + "compilerOptions": { + "sourceMap": true, + "inlineSources": true, + "sourceRoot": "/", + "outDir": "dist", + "baseUrl": "./src", + "paths": { + "~/*": ["./*"], + "@/*": ["/*"] + } + } +} diff --git a/packages/app-env/package.json b/packages/app-env/package.json index 984df8e0f..2da59a013 100644 --- a/packages/app-env/package.json +++ b/packages/app-env/package.json @@ -9,10 +9,10 @@ "types": "./src/index.ts", "dependencies": { "viem": "latest", - "app-contracts": "workspace:*" + "@repo/contracts": "workspace:*" }, "devDependencies": { - "@repo/typescript-config": "workspace:*", + "@repo/tsconfig": "workspace:*", "typescript": "^5.3.3" } } diff --git a/packages/app-env/src/chains.ts b/packages/app-env/src/chains.ts index ee618c150..f5f9653fb 100644 --- a/packages/app-env/src/chains.ts +++ b/packages/app-env/src/chains.ts @@ -1,23 +1,13 @@ import type { Chain } from 'viem' import { arbitrum, - aurora, avalanche, base, bsc, - celo, - cronos, - fantom, - gnosis, - harmonyOne, - kava, mainnet, - metis, - moonbeam, optimism, polygon, sepolia, - zkSync, } from 'viem/chains' export const eosEvmTestnet: Chain = { @@ -41,26 +31,19 @@ export const eosEvmTestnet: Chain = { testnet: true, } -const prodChains: Chain[] = [ - arbitrum, - avalanche, +export const prodChains: Chain[] = [ base, - celo, - mainnet, + arbitrum, optimism, polygon, - zkSync, - bsc, - fantom, - moonbeam, - cronos, - kava, - metis, - gnosis, - aurora, - harmonyOne, + mainnet, // Ethereum + avalanche, + bsc, // BNB Chain ] -const devChains: Chain[] = [eosEvmTestnet, sepolia] + +// Note: Solana is not included as it's not an EVM-compatible chain and not supported by viem + +export const devChains: Chain[] = [eosEvmTestnet, sepolia] // note: use .entries() to get an array export const appChains = { diff --git a/packages/app-env/src/env.ts b/packages/app-env/src/env.ts index e61719b80..705229046 100644 --- a/packages/app-env/src/env.ts +++ b/packages/app-env/src/env.ts @@ -7,7 +7,7 @@ import { type TokenContractData, usdcContracts, usdtContracts, -} from 'app-contracts' +} from '@repo/contracts' import type { Address, Chain } from 'viem' import { appChains } from './chains' diff --git a/packages/contracts/README.md b/packages/contracts/README.md new file mode 100644 index 000000000..823b520cd --- /dev/null +++ b/packages/contracts/README.md @@ -0,0 +1,15 @@ +# @repo/contracts + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run ./src/index.ts +``` + +This project was created using `bun init` in bun v1.1.24. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/packages/app-contracts/package.json b/packages/contracts/package.json similarity index 70% rename from packages/app-contracts/package.json rename to packages/contracts/package.json index 11c93a877..072dde042 100644 --- a/packages/app-contracts/package.json +++ b/packages/contracts/package.json @@ -1,5 +1,5 @@ { - "name": "app-contracts", + "name": "@repo/contracts", "version": "0.0.1", "private": true, "description": "Smartsale Smart Contracts Data", @@ -12,7 +12,8 @@ "viem": "latest" }, "devDependencies": { - "@repo/typescript-config": "workspace:*", - "typescript": "^5.3.3" + "@repo/tsconfig": "workspace:*", + "typescript": "^5.3.3", + "@types/bun": "latest" } } diff --git a/packages/app-contracts/src/dev/auction/testnet-allow-list.ts b/packages/contracts/src/dev/auction/testnet-allow-list.ts similarity index 100% rename from packages/app-contracts/src/dev/auction/testnet-allow-list.ts rename to packages/contracts/src/dev/auction/testnet-allow-list.ts diff --git a/packages/app-contracts/src/dev/auction/testnet-deposit-order.ts b/packages/contracts/src/dev/auction/testnet-deposit-order.ts similarity index 100% rename from packages/app-contracts/src/dev/auction/testnet-deposit-order.ts rename to packages/contracts/src/dev/auction/testnet-deposit-order.ts diff --git a/packages/app-contracts/src/dev/auction/testnet-easy-auction.ts b/packages/contracts/src/dev/auction/testnet-easy-auction.ts similarity index 100% rename from packages/app-contracts/src/dev/auction/testnet-easy-auction.ts rename to packages/contracts/src/dev/auction/testnet-easy-auction.ts diff --git a/packages/app-contracts/src/dev/index.ts b/packages/contracts/src/dev/index.ts similarity index 100% rename from packages/app-contracts/src/dev/index.ts rename to packages/contracts/src/dev/index.ts diff --git a/packages/app-contracts/src/dev/tokens/eos-fake-bitusd.ts b/packages/contracts/src/dev/tokens/eos-fake-bitusd.ts similarity index 100% rename from packages/app-contracts/src/dev/tokens/eos-fake-bitusd.ts rename to packages/contracts/src/dev/tokens/eos-fake-bitusd.ts diff --git a/packages/app-contracts/src/dev/tokens/eos-fake-usdt.ts b/packages/contracts/src/dev/tokens/eos-fake-usdt.ts similarity index 100% rename from packages/app-contracts/src/dev/tokens/eos-fake-usdt.ts rename to packages/contracts/src/dev/tokens/eos-fake-usdt.ts diff --git a/packages/app-contracts/src/dev/tokens/sepolia-usdt.ts b/packages/contracts/src/dev/tokens/sepolia-usdt.ts similarity index 100% rename from packages/app-contracts/src/dev/tokens/sepolia-usdt.ts rename to packages/contracts/src/dev/tokens/sepolia-usdt.ts diff --git a/packages/app-contracts/src/dev/tokens/testnet-blpl.ts b/packages/contracts/src/dev/tokens/testnet-blpl.ts similarity index 100% rename from packages/app-contracts/src/dev/tokens/testnet-blpl.ts rename to packages/contracts/src/dev/tokens/testnet-blpl.ts diff --git a/packages/app-contracts/src/dev/tokens/testnet-mbots-prelaunch.ts b/packages/contracts/src/dev/tokens/testnet-mbots-prelaunch.ts similarity index 100% rename from packages/app-contracts/src/dev/tokens/testnet-mbots-prelaunch.ts rename to packages/contracts/src/dev/tokens/testnet-mbots-prelaunch.ts diff --git a/packages/app-contracts/src/dev/tokens/testnet-usd-cred.ts b/packages/contracts/src/dev/tokens/testnet-usd-cred.ts similarity index 100% rename from packages/app-contracts/src/dev/tokens/testnet-usd-cred.ts rename to packages/contracts/src/dev/tokens/testnet-usd-cred.ts diff --git a/packages/app-contracts/src/dev/tokens/testnet-usdt.ts b/packages/contracts/src/dev/tokens/testnet-usdt.ts similarity index 100% rename from packages/app-contracts/src/dev/tokens/testnet-usdt.ts rename to packages/contracts/src/dev/tokens/testnet-usdt.ts diff --git a/packages/app-contracts/src/index.ts b/packages/contracts/src/index.ts similarity index 100% rename from packages/app-contracts/src/index.ts rename to packages/contracts/src/index.ts diff --git a/packages/app-contracts/src/prod/index.ts b/packages/contracts/src/prod/index.ts similarity index 100% rename from packages/app-contracts/src/prod/index.ts rename to packages/contracts/src/prod/index.ts diff --git a/packages/app-contracts/src/prod/tokens/eos-bitusd.ts b/packages/contracts/src/prod/tokens/eos-bitusd.ts similarity index 100% rename from packages/app-contracts/src/prod/tokens/eos-bitusd.ts rename to packages/contracts/src/prod/tokens/eos-bitusd.ts diff --git a/packages/app-contracts/src/prod/tokens/eos-usdt.ts b/packages/contracts/src/prod/tokens/eos-usdt.ts similarity index 100% rename from packages/app-contracts/src/prod/tokens/eos-usdt.ts rename to packages/contracts/src/prod/tokens/eos-usdt.ts diff --git a/packages/app-contracts/src/prod/tokens/usdc.ts b/packages/contracts/src/prod/tokens/usdc.ts similarity index 100% rename from packages/app-contracts/src/prod/tokens/usdc.ts rename to packages/contracts/src/prod/tokens/usdc.ts diff --git a/packages/app-contracts/src/prod/tokens/usdt.ts b/packages/contracts/src/prod/tokens/usdt.ts similarity index 100% rename from packages/app-contracts/src/prod/tokens/usdt.ts rename to packages/contracts/src/prod/tokens/usdt.ts diff --git a/packages/app-contracts/src/types.ts b/packages/contracts/src/types.ts similarity index 99% rename from packages/app-contracts/src/types.ts rename to packages/contracts/src/types.ts index 21be7a77a..85f86698e 100644 --- a/packages/app-contracts/src/types.ts +++ b/packages/contracts/src/types.ts @@ -1,5 +1,6 @@ import type { Abi, Address } from 'abitype' import type { Chain } from 'viem' + export interface ContractData { abi: Abi address: Address | string @@ -8,6 +9,7 @@ export interface ContractData { chainName: string indexFromBlock: number } + export interface TokenContractData extends ContractData { name: string symbol: string diff --git a/packages/app-contracts/tsconfig.json b/packages/contracts/tsconfig.json similarity index 100% rename from packages/app-contracts/tsconfig.json rename to packages/contracts/tsconfig.json diff --git a/packages/jobs/.env-sample b/packages/jobs/.env-sample new file mode 100644 index 000000000..5ffc4485d --- /dev/null +++ b/packages/jobs/.env-sample @@ -0,0 +1,5 @@ +DATABASE_URL="your_supabase_postgres_url" +SEPOLIA_RPC=https://eth-sepolia.g.alchemy.com/v2/xxx +ISSUER_KEY=xxx +ISSUER_ADDRESS=0x +PRESALE_ADDRESS=0x \ No newline at end of file diff --git a/packages/jobs/.gitignore b/packages/jobs/.gitignore new file mode 100644 index 000000000..6524f048d --- /dev/null +++ b/packages/jobs/.gitignore @@ -0,0 +1 @@ +.trigger \ No newline at end of file diff --git a/packages/jobs/README.md b/packages/jobs/README.md new file mode 100644 index 000000000..826636969 --- /dev/null +++ b/packages/jobs/README.md @@ -0,0 +1,23 @@ +# Trigger.dev Background Jobs + +This directory contains the configuration and implementation of background jobs using Trigger.dev for the Basilica project. + +## Overview + +Trigger.dev is an open-source job scheduling and execution platform that allows us to create, manage, and monitor background jobs efficiently. We use it for various issuance-related tasks and to handle webhook events from services like Moralis and Alchemy. + +## Key Features + +- **Webhook Integration**: Trigger.dev provides URLs that can be called from external webhooks, allowing us to process events from Moralis and Alchemy Web3 hooks. +- **Job Scheduling**: Easily schedule and manage recurring or one-time jobs. +- **Open Source**: Trigger.dev is open-source, providing flexibility and the option for self-hosting in the future if needed. +- **Cost-Effective**: Offers a robust feature set at a competitive price point. + +## Usage + +For detailed instructions on setting up and using Trigger.dev jobs, please refer to the official Trigger.dev documentation: + +[Trigger.dev Documentation](https://trigger.dev/docs) + +This comprehensive guide covers everything from creating your first job to advanced usage and best practices. + diff --git a/packages/jobs/package.json b/packages/jobs/package.json new file mode 100644 index 000000000..7eaf5fb89 --- /dev/null +++ b/packages/jobs/package.json @@ -0,0 +1,17 @@ +{ + "name": "@repo/jobs", + "version": "0.0.1", + "private": true, + "main": "src/index.ts", + "scripts": { + "dev": "bunx trigger.dev@beta dev", + "deploy:staging": "bunx trigger.dev@beta deploy --env staging", + "deploy:prod": "bunx trigger.dev@beta deploy --env prod" + }, + "dependencies": { + "@repo/alchemy": "workspace:*", + "@trigger.dev/sdk": "3.0.0-beta.55", + "alchemy-sdk": "^3.4.1", + "viem": "latest" + } +} diff --git a/packages/jobs/src/config.ts b/packages/jobs/src/config.ts new file mode 100644 index 000000000..44a043319 --- /dev/null +++ b/packages/jobs/src/config.ts @@ -0,0 +1,33 @@ +import type { Address } from 'viem' +import { isAddress } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { z } from 'zod' + +const envSchema = z.object({ + ISSUER_KEY: z + .string() + .min(1) + .length(64) + .regex(/^[a-f0-9]+$/i, 'Invalid issuer key format'), + ISSUER_ADDRESS: z + .string() + .refine( + (value): value is Address => isAddress(value), + 'Invalid issuer address', + ), +}) + +const parsedEnv = envSchema.safeParse(process.env) +if (!parsedEnv.success) { + console.error( + `Environment validation failed: ${JSON.stringify(parsedEnv.error.format())}`, + ) + process.exit(1) +} + +export const appConfig = { + eosEvmApi: 'https://api.testnet.evm.eosnetwork.com', + issuerKey: parsedEnv.data.ISSUER_KEY, + issuerAddress: parsedEnv.data.ISSUER_ADDRESS, + issuerAccount: privateKeyToAccount(`0x${parsedEnv.data.ISSUER_KEY}`), +} diff --git a/packages/jobs/src/index.ts b/packages/jobs/src/index.ts new file mode 100644 index 000000000..813c48b07 --- /dev/null +++ b/packages/jobs/src/index.ts @@ -0,0 +1 @@ +export * from './trigger/activity' diff --git a/packages/jobs/src/lib/presale-issuer.ts b/packages/jobs/src/lib/presale-issuer.ts new file mode 100644 index 000000000..c98642e3e --- /dev/null +++ b/packages/jobs/src/lib/presale-issuer.ts @@ -0,0 +1,44 @@ +// import { TestnetBLPL } from '@repo/contracts' +import { createWalletClient, erc20Abi, formatUnits } from 'viem' +import { http, type Address } from 'viem' +import { appConfig } from '../config' +import { eosEvmTestnet } from '../tmp' + +/** + * Issues presale tokens to a specified address. + * @param to The address to receive the presale tokens + * @param amount The amount of presale tokens to issue + */ +export async function issuePresaleTokens(to: Address, amount: bigint) { + try { + const walletClient = createWalletClient({ + chain: eosEvmTestnet, + transport: http(), + key: appConfig.issuerKey, + account: appConfig.issuerAccount, + }) + const trxHash = await walletClient.writeContract({ + address: TestnetBLPL.address, + abi: TestnetBLPL.abi, + functionName: 'transfer', + args: [to, amount], + }) + return `Issued ${formatUnits(amount, 6)} tokens to ${to} on transaction ${trxHash}` + } catch (error) { + console.log((error as Error).message) + return null + } +} + +export const TestnetBLPL = { + address: '0x2BF8feebD09B2520E69f27294768774544c98985', + name: 'Bitlauncher Prelaunch Token', + symbol: 'BLPL', + decimals: 18, + indexFromBlock: 30051449, + chainId: 15557, // eos_evm + chainType: 'evm', + chainName: 'EOS EVM Tesnet', + chain: eosEvmTestnet, + abi: erc20Abi, +} as const diff --git a/packages/jobs/src/tmp/chains.ts b/packages/jobs/src/tmp/chains.ts new file mode 100644 index 000000000..f5f9653fb --- /dev/null +++ b/packages/jobs/src/tmp/chains.ts @@ -0,0 +1,60 @@ +import type { Chain } from 'viem' +import { + arbitrum, + avalanche, + base, + bsc, + mainnet, + optimism, + polygon, + sepolia, +} from 'viem/chains' + +export const eosEvmTestnet: Chain = { + nativeCurrency: { + name: 'EOS', + symbol: 'EOS', + decimals: 18, + }, + id: 15557, + name: 'EOS EVM Testnet', + rpcUrls: { + default: { http: ['https://api.testnet.evm.eosnetwork.com/'] }, + public: { http: ['https://api.testnet.evm.eosnetwork.com/'] }, + }, + blockExplorers: { + default: { + name: 'EOS EVM Testnet Explorer', + url: 'https://explorer.testnet.evm.eosnetwork.com/', + }, + }, + testnet: true, +} + +export const prodChains: Chain[] = [ + base, + arbitrum, + optimism, + polygon, + mainnet, // Ethereum + avalanche, + bsc, // BNB Chain +] + +// Note: Solana is not included as it's not an EVM-compatible chain and not supported by viem + +export const devChains: Chain[] = [eosEvmTestnet, sepolia] + +// note: use .entries() to get an array +export const appChains = { + dev: createMapFromId(devChains), + prod: createMapFromId(prodChains), +} as const + +function createMapFromId(items: Chain[]): Map { + const mapFromId = new Map() + + items.forEach((item) => mapFromId.set(item.id, item)) + + return mapFromId +} diff --git a/packages/jobs/src/tmp/index.ts b/packages/jobs/src/tmp/index.ts new file mode 100644 index 000000000..8bf871d98 --- /dev/null +++ b/packages/jobs/src/tmp/index.ts @@ -0,0 +1,2 @@ +export * from './chains' +// export * from './env' diff --git a/packages/jobs/src/trigger/activity.ts b/packages/jobs/src/trigger/activity.ts new file mode 100644 index 000000000..4e92d4482 --- /dev/null +++ b/packages/jobs/src/trigger/activity.ts @@ -0,0 +1,35 @@ +import type { AlchemyActivity, AlchemyWebhookEvent } from '@repo/alchemy' +import { logger, task } from '@trigger.dev/sdk/v3' +import { isAddress, parseUnits } from 'viem' +import { issuePresaleTokens } from '../lib/presale-issuer' + +const STABLECOIN_DECIMALS = 6 + +export const addressActivityTask = task({ + id: 'address-activity', + run: async (payload: AlchemyWebhookEvent) => { + try { + const activity: AlchemyActivity = payload.event.activity[0] + console.log(activity) + + if (!isAddress(activity.toAddress)) + throw new Error(`Invalid to address: ${activity.toAddress}`) + + const valueInTokenUnits = parseUnits( + activity.value.toString(), + STABLECOIN_DECIMALS, + ) + + const result = await issuePresaleTokens( + activity.toAddress, + valueInTokenUnits, + ) + console.log(result) + } catch (error) { + logger.error('Error processing address activity', { + error: error instanceof Error ? error.message : String(error), + }) + throw error + } + }, +}) diff --git a/packages/jobs/src/trigger/example.ts b/packages/jobs/src/trigger/example.ts new file mode 100644 index 000000000..0529c873f --- /dev/null +++ b/packages/jobs/src/trigger/example.ts @@ -0,0 +1,14 @@ +import { logger, task, wait } from '@trigger.dev/sdk/v3' + +export const helloWorldTask = task({ + id: 'hello-world', + run: async (payload: any, { ctx }) => { + logger.log('Hello, world!', { payload, ctx }) + + await wait.for({ seconds: 5 }) + + return { + message: 'Hello, world!', + } + }, +}) diff --git a/packages/jobs/trigger.config.ts b/packages/jobs/trigger.config.ts new file mode 100644 index 000000000..191907b0b --- /dev/null +++ b/packages/jobs/trigger.config.ts @@ -0,0 +1,16 @@ +import type { TriggerConfig } from '@trigger.dev/sdk/v3' + +export const config: TriggerConfig = { + project: 'proj_uefmifhkitjdldujpocd', + logLevel: 'log', + retries: { + enabledInDev: true, + default: { + maxAttempts: 3, + minTimeoutInMs: 1000, + maxTimeoutInMs: 10000, + factor: 2, + randomize: true, + }, + }, +} diff --git a/packages/jobs/tsconfig.json b/packages/jobs/tsconfig.json new file mode 100644 index 000000000..3cc54f7e5 --- /dev/null +++ b/packages/jobs/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../packages/tsconfig/node16.json", + "include": ["./src/**/*", "src/lib/utils.ts", "trigger.config.ts"], + "exclude": ["node_modules"], + "compilerOptions": { + "sourceMap": true, + "inlineSources": true, + "sourceRoot": "/", + "outDir": "dist", + "baseUrl": "./src", + "paths": { + "~/*": ["./*"], + "@/*": ["./*"] + } + } +} diff --git a/apps/supabase/.gitignore b/packages/supabase/.gitignore similarity index 100% rename from apps/supabase/.gitignore rename to packages/supabase/.gitignore diff --git a/apps/supabase/README.md b/packages/supabase/README.md similarity index 100% rename from apps/supabase/README.md rename to packages/supabase/README.md diff --git a/apps/supabase/config.toml b/packages/supabase/config.toml similarity index 100% rename from apps/supabase/config.toml rename to packages/supabase/config.toml diff --git a/apps/supabase/migrations/20240414235435_remote_schema.sql b/packages/supabase/migrations/20240414235435_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240414235435_remote_schema.sql rename to packages/supabase/migrations/20240414235435_remote_schema.sql diff --git a/apps/supabase/migrations/20240417041423_remote_schema.sql b/packages/supabase/migrations/20240417041423_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240417041423_remote_schema.sql rename to packages/supabase/migrations/20240417041423_remote_schema.sql diff --git a/apps/supabase/migrations/20240417153529_remote_schema.sql b/packages/supabase/migrations/20240417153529_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240417153529_remote_schema.sql rename to packages/supabase/migrations/20240417153529_remote_schema.sql diff --git a/apps/supabase/migrations/20240418165957_remote_schema.sql b/packages/supabase/migrations/20240418165957_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240418165957_remote_schema.sql rename to packages/supabase/migrations/20240418165957_remote_schema.sql diff --git a/apps/supabase/migrations/20240418231216_remote_schema.sql b/packages/supabase/migrations/20240418231216_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240418231216_remote_schema.sql rename to packages/supabase/migrations/20240418231216_remote_schema.sql diff --git a/apps/supabase/migrations/20240419001509_remote_schema.sql b/packages/supabase/migrations/20240419001509_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240419001509_remote_schema.sql rename to packages/supabase/migrations/20240419001509_remote_schema.sql diff --git a/apps/supabase/migrations/20240419003028_remote_schema.sql b/packages/supabase/migrations/20240419003028_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240419003028_remote_schema.sql rename to packages/supabase/migrations/20240419003028_remote_schema.sql diff --git a/apps/supabase/migrations/20240419010728_remote_schema.sql b/packages/supabase/migrations/20240419010728_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240419010728_remote_schema.sql rename to packages/supabase/migrations/20240419010728_remote_schema.sql diff --git a/apps/supabase/migrations/20240719192750_remote_schema.sql b/packages/supabase/migrations/20240719192750_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240719192750_remote_schema.sql rename to packages/supabase/migrations/20240719192750_remote_schema.sql diff --git a/apps/supabase/migrations/20240720155732_remote_schema.sql b/packages/supabase/migrations/20240720155732_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240720155732_remote_schema.sql rename to packages/supabase/migrations/20240720155732_remote_schema.sql diff --git a/apps/supabase/migrations/20240805231931_remote_schema.sql b/packages/supabase/migrations/20240805231931_remote_schema.sql similarity index 100% rename from apps/supabase/migrations/20240805231931_remote_schema.sql rename to packages/supabase/migrations/20240805231931_remote_schema.sql diff --git a/packages/supabase/migrations/20240824213342_remote_schema.sql b/packages/supabase/migrations/20240824213342_remote_schema.sql new file mode 100644 index 000000000..6a80e1d23 --- /dev/null +++ b/packages/supabase/migrations/20240824213342_remote_schema.sql @@ -0,0 +1,274 @@ +create type "public"."chain_type" as enum ('evm', 'eos', 'solana', 'cosmos'); + +create type "public"."trx_type" as enum ('presale_deposit', 'usdcred_deposit', 'usdcred_withdrawal'); + +revoke delete on table "public"."user" from "anon"; + +revoke insert on table "public"."user" from "anon"; + +revoke references on table "public"."user" from "anon"; + +revoke select on table "public"."user" from "anon"; + +revoke trigger on table "public"."user" from "anon"; + +revoke truncate on table "public"."user" from "anon"; + +revoke update on table "public"."user" from "anon"; + +revoke delete on table "public"."user" from "authenticated"; + +revoke insert on table "public"."user" from "authenticated"; + +revoke references on table "public"."user" from "authenticated"; + +revoke select on table "public"."user" from "authenticated"; + +revoke trigger on table "public"."user" from "authenticated"; + +revoke truncate on table "public"."user" from "authenticated"; + +revoke update on table "public"."user" from "authenticated"; + +revoke delete on table "public"."user" from "service_role"; + +revoke insert on table "public"."user" from "service_role"; + +revoke references on table "public"."user" from "service_role"; + +revoke select on table "public"."user" from "service_role"; + +revoke trigger on table "public"."user" from "service_role"; + +revoke truncate on table "public"."user" from "service_role"; + +revoke update on table "public"."user" from "service_role"; + +alter table "public"."user" drop constraint "users_pkey"; + +drop index if exists "public"."users_pkey"; + +drop table "public"."user"; + +create table "public"."account" ( + "created_at" timestamp(6) with time zone not null default CURRENT_TIMESTAMP, + "short_link" text, + "account" text not null +); + + +create table "public"."presale_deposit" ( + "id" uuid not null default gen_random_uuid(), + "created_at" timestamp with time zone not null default now(), + "presale_id" bigint, + "deposit_hash" text, + "issuance_hash" text, + "amount" bigint +); + + +create table "public"."transaction" ( + "hash" text not null, + "created_at" timestamp with time zone not null default now(), + "chain_type" chain_type, + "chain_id" bigint, + "trx_type" trx_type, + "final" boolean default false +); + + +alter table "public"."auction" add column "project_id" bigint; + +alter table "public"."presale" drop column "signature"; + +alter table "public"."presale" add column "end_timestamptz" timestamp with time zone; + +alter table "public"."presale" add column "fundraising_goal" bigint; + +alter table "public"."presale" add column "max_allocation" bigint; + +alter table "public"."presale" add column "start_timestamptz" timestamp with time zone; + +alter table "public"."project" disable row level security; + +alter table "public"."transfer" drop column "chain_id"; + +alter table "public"."transfer" drop column "created_at"; + +alter table "public"."whitelist" add column "signed_message" text; + +CREATE UNIQUE INDEX presale_deposits_pkey ON public.presale_deposit USING btree (id); + +CREATE UNIQUE INDEX transactions_pkey ON public.transaction USING btree (hash); + +CREATE UNIQUE INDEX user_account_key ON public.account USING btree (account); + +CREATE UNIQUE INDEX user_pkey ON public.account USING btree (account); + +CREATE UNIQUE INDEX "user_shortLinks_key" ON public.account USING btree (short_link); + +alter table "public"."account" add constraint "user_pkey" PRIMARY KEY using index "user_pkey"; + +alter table "public"."presale_deposit" add constraint "presale_deposits_pkey" PRIMARY KEY using index "presale_deposits_pkey"; + +alter table "public"."transaction" add constraint "transactions_pkey" PRIMARY KEY using index "transactions_pkey"; + +alter table "public"."account" add constraint "user_account_key" UNIQUE using index "user_account_key"; + +alter table "public"."account" add constraint "user_shortLinks_key" UNIQUE using index "user_shortLinks_key"; + +alter table "public"."auction" add constraint "auction_project_id_fkey" FOREIGN KEY (project_id) REFERENCES project(id) not valid; + +alter table "public"."auction" validate constraint "auction_project_id_fkey"; + +alter table "public"."presale" add constraint "presale_project_id_fkey" FOREIGN KEY (project_id) REFERENCES project(id) not valid; + +alter table "public"."presale" validate constraint "presale_project_id_fkey"; + +alter table "public"."presale_deposit" add constraint "presale_deposit_deposit_hash_fkey" FOREIGN KEY (deposit_hash) REFERENCES transaction(hash) not valid; + +alter table "public"."presale_deposit" validate constraint "presale_deposit_deposit_hash_fkey"; + +alter table "public"."presale_deposit" add constraint "presale_deposit_issuance_hash_fkey" FOREIGN KEY (issuance_hash) REFERENCES transaction(hash) not valid; + +alter table "public"."presale_deposit" validate constraint "presale_deposit_issuance_hash_fkey"; + +alter table "public"."presale_deposit" add constraint "presale_deposit_presale_id_fkey" FOREIGN KEY (presale_id) REFERENCES presale(id) not valid; + +alter table "public"."presale_deposit" validate constraint "presale_deposit_presale_id_fkey"; + +alter table "public"."presale_deposit" add constraint "presale_deposits_presale_id_fkey" FOREIGN KEY (presale_id) REFERENCES presale(id) not valid; + +alter table "public"."presale_deposit" validate constraint "presale_deposits_presale_id_fkey"; + +alter table "public"."whitelist" add constraint "whitelist_project_id_fkey" FOREIGN KEY (project_id) REFERENCES project(id) not valid; + +alter table "public"."whitelist" validate constraint "whitelist_project_id_fkey"; + +grant delete on table "public"."account" to "anon"; + +grant insert on table "public"."account" to "anon"; + +grant references on table "public"."account" to "anon"; + +grant select on table "public"."account" to "anon"; + +grant trigger on table "public"."account" to "anon"; + +grant truncate on table "public"."account" to "anon"; + +grant update on table "public"."account" to "anon"; + +grant delete on table "public"."account" to "authenticated"; + +grant insert on table "public"."account" to "authenticated"; + +grant references on table "public"."account" to "authenticated"; + +grant select on table "public"."account" to "authenticated"; + +grant trigger on table "public"."account" to "authenticated"; + +grant truncate on table "public"."account" to "authenticated"; + +grant update on table "public"."account" to "authenticated"; + +grant delete on table "public"."account" to "service_role"; + +grant insert on table "public"."account" to "service_role"; + +grant references on table "public"."account" to "service_role"; + +grant select on table "public"."account" to "service_role"; + +grant trigger on table "public"."account" to "service_role"; + +grant truncate on table "public"."account" to "service_role"; + +grant update on table "public"."account" to "service_role"; + +grant delete on table "public"."presale_deposit" to "anon"; + +grant insert on table "public"."presale_deposit" to "anon"; + +grant references on table "public"."presale_deposit" to "anon"; + +grant select on table "public"."presale_deposit" to "anon"; + +grant trigger on table "public"."presale_deposit" to "anon"; + +grant truncate on table "public"."presale_deposit" to "anon"; + +grant update on table "public"."presale_deposit" to "anon"; + +grant delete on table "public"."presale_deposit" to "authenticated"; + +grant insert on table "public"."presale_deposit" to "authenticated"; + +grant references on table "public"."presale_deposit" to "authenticated"; + +grant select on table "public"."presale_deposit" to "authenticated"; + +grant trigger on table "public"."presale_deposit" to "authenticated"; + +grant truncate on table "public"."presale_deposit" to "authenticated"; + +grant update on table "public"."presale_deposit" to "authenticated"; + +grant delete on table "public"."presale_deposit" to "service_role"; + +grant insert on table "public"."presale_deposit" to "service_role"; + +grant references on table "public"."presale_deposit" to "service_role"; + +grant select on table "public"."presale_deposit" to "service_role"; + +grant trigger on table "public"."presale_deposit" to "service_role"; + +grant truncate on table "public"."presale_deposit" to "service_role"; + +grant update on table "public"."presale_deposit" to "service_role"; + +grant delete on table "public"."transaction" to "anon"; + +grant insert on table "public"."transaction" to "anon"; + +grant references on table "public"."transaction" to "anon"; + +grant select on table "public"."transaction" to "anon"; + +grant trigger on table "public"."transaction" to "anon"; + +grant truncate on table "public"."transaction" to "anon"; + +grant update on table "public"."transaction" to "anon"; + +grant delete on table "public"."transaction" to "authenticated"; + +grant insert on table "public"."transaction" to "authenticated"; + +grant references on table "public"."transaction" to "authenticated"; + +grant select on table "public"."transaction" to "authenticated"; + +grant trigger on table "public"."transaction" to "authenticated"; + +grant truncate on table "public"."transaction" to "authenticated"; + +grant update on table "public"."transaction" to "authenticated"; + +grant delete on table "public"."transaction" to "service_role"; + +grant insert on table "public"."transaction" to "service_role"; + +grant references on table "public"."transaction" to "service_role"; + +grant select on table "public"."transaction" to "service_role"; + +grant trigger on table "public"."transaction" to "service_role"; + +grant truncate on table "public"."transaction" to "service_role"; + +grant update on table "public"."transaction" to "service_role"; + + diff --git a/packages/supabase/migrations/20240825040214_remote_schema.sql b/packages/supabase/migrations/20240825040214_remote_schema.sql new file mode 100644 index 000000000..9dbf0296a --- /dev/null +++ b/packages/supabase/migrations/20240825040214_remote_schema.sql @@ -0,0 +1,33 @@ +alter table "public"."esr" alter column "account" set not null; + +alter table "public"."esr" alter column "code" set not null; + +alter table "public"."esr" alter column "trx_id" set not null; + +alter table "public"."presale" alter column "account" set not null; + +alter table "public"."presale" alter column "address" set not null; + +alter table "public"."presale" alter column "end_timestamptz" set not null; + +alter table "public"."presale" alter column "fundraising_goal" set not null; + +alter table "public"."presale" alter column "max_allocation" set not null; + +alter table "public"."presale" alter column "project_id" set not null; + +alter table "public"."presale" alter column "start_timestamptz" set not null; + +alter table "public"."presale_deposit" alter column "amount" set not null; + +alter table "public"."presale_deposit" alter column "deposit_hash" set not null; + +alter table "public"."presale_deposit" alter column "presale_id" set not null; + +alter table "public"."project" alter column "name" set not null; + +alter table "public"."project" alter column "pitch" set not null; + +alter table "public"."whitelist" alter column "signed_message" set not null; + + diff --git a/packages/supabase/migrations/20240825181232_remote_schema.sql b/packages/supabase/migrations/20240825181232_remote_schema.sql new file mode 100644 index 000000000..d3b95817a --- /dev/null +++ b/packages/supabase/migrations/20240825181232_remote_schema.sql @@ -0,0 +1,5 @@ +alter table "public"."presale" add column "close_timestampz" timestamp with time zone; + +alter table "public"."presale" add column "total_raised" bigint not null default '0'::bigint; + + diff --git a/apps/supabase/package.json b/packages/supabase/package.json similarity index 92% rename from apps/supabase/package.json rename to packages/supabase/package.json index dcbee76d4..c991b1fc5 100644 --- a/apps/supabase/package.json +++ b/packages/supabase/package.json @@ -2,6 +2,7 @@ "name": "@repo/supabase", "version": "0.0.1", "private": true, + "type": "module", "description": "supabase module for smartevm", "main": "./src/index.ts", "types": "./src/index.ts", @@ -15,7 +16,7 @@ "license": "ISC", "dependencies": {}, "devDependencies": { - "@repo/typescript-config": "workspace:*", + "@repo/tsconfig": "workspace:*", "supabase-to-zod": "^1.0.7", "@faker-js/faker": "^8.4.1", "supabase": "^1.187.3" diff --git a/apps/supabase/scripts/fake-orders.ts b/packages/supabase/scripts/fake-orders.ts similarity index 100% rename from apps/supabase/scripts/fake-orders.ts rename to packages/supabase/scripts/fake-orders.ts diff --git a/apps/supabase/seed.sql b/packages/supabase/seed.sql similarity index 100% rename from apps/supabase/seed.sql rename to packages/supabase/seed.sql diff --git a/apps/supabase/src/index.ts b/packages/supabase/src/index.ts similarity index 100% rename from apps/supabase/src/index.ts rename to packages/supabase/src/index.ts diff --git a/apps/supabase/src/supa.schemas.ts b/packages/supabase/src/supa.schemas.ts similarity index 61% rename from apps/supabase/src/supa.schemas.ts rename to packages/supabase/src/supa.schemas.ts index ec3ed42d4..342c3856f 100644 --- a/apps/supabase/src/supa.schemas.ts +++ b/packages/supabase/src/supa.schemas.ts @@ -1,6 +1,6 @@ // Generated by ts-to-zod -import { z } from 'zod' -import type { Json } from './supa.types' +import { z } from "zod"; +import { Json } from "./supa.types"; export const jsonSchema: z.ZodSchema = z.lazy(() => z @@ -12,7 +12,27 @@ export const jsonSchema: z.ZodSchema = z.lazy(() => z.array(jsonSchema), ]) .nullable(), -) +); + +export const accountRowSchema = z.object({ + account: z.string(), + created_at: z.string(), + short_link: z.string().nullable(), +}); + +export const accountInsertSchema = z.object({ + account: z.string(), + created_at: z.string().optional(), + short_link: z.string().optional().nullable(), +}); + +export const accountUpdateSchema = z.object({ + account: z.string().optional(), + created_at: z.string().optional(), + short_link: z.string().optional().nullable(), +}); + +export const accountRelationshipsSchema = z.tuple([]); export const auctionRowSchema = z.object({ address_auctioning_token: z.string().nullable(), @@ -36,11 +56,12 @@ export const auctionRowSchema = z.object({ min_funding_threshold: z.number().nullable(), minimum_bidding_amount_per_order: z.number().nullable(), order_cancellation_end_date: z.string().nullable(), + project_id: z.number().nullable(), starting_time_stamp: z.string().nullable(), symbol_auctioning_token: z.string().nullable(), symbol_bidding_token: z.string().nullable(), usd_amount_traded: z.number().nullable(), -}) +}); export const auctionInsertSchema = z.object({ address_auctioning_token: z.string().optional().nullable(), @@ -64,11 +85,12 @@ export const auctionInsertSchema = z.object({ min_funding_threshold: z.number().optional().nullable(), minimum_bidding_amount_per_order: z.number().optional().nullable(), order_cancellation_end_date: z.string().optional().nullable(), + project_id: z.number().optional().nullable(), starting_time_stamp: z.string().optional().nullable(), symbol_auctioning_token: z.string().optional().nullable(), symbol_bidding_token: z.string().optional().nullable(), usd_amount_traded: z.number().optional().nullable(), -}) +}); export const auctionUpdateSchema = z.object({ address_auctioning_token: z.string().optional().nullable(), @@ -92,56 +114,65 @@ export const auctionUpdateSchema = z.object({ min_funding_threshold: z.number().optional().nullable(), minimum_bidding_amount_per_order: z.number().optional().nullable(), order_cancellation_end_date: z.string().optional().nullable(), + project_id: z.number().optional().nullable(), starting_time_stamp: z.string().optional().nullable(), symbol_auctioning_token: z.string().optional().nullable(), symbol_bidding_token: z.string().optional().nullable(), usd_amount_traded: z.number().optional().nullable(), -}) - -export const auctionRelationshipsSchema = z.tuple([]) +}); + +export const auctionRelationshipsSchema = z.tuple([ + z.object({ + foreignKeyName: z.literal("auction_project_id_fkey"), + columns: z.tuple([z.literal("project_id")]), + isOneToOne: z.literal(false), + referencedRelation: z.literal("project"), + referencedColumns: z.tuple([z.literal("id")]), + }), +]); export const esrRowSchema = z.object({ - account: z.string().nullable(), - code: z.string().nullable(), + account: z.string(), + code: z.string(), created_at: z.string(), id: z.string(), - trx_id: z.string().nullable(), -}) + trx_id: z.string(), +}); export const esrInsertSchema = z.object({ - account: z.string().optional().nullable(), - code: z.string().optional().nullable(), + account: z.string(), + code: z.string(), created_at: z.string().optional(), id: z.string().optional(), - trx_id: z.string().optional().nullable(), -}) + trx_id: z.string(), +}); export const esrUpdateSchema = z.object({ - account: z.string().optional().nullable(), - code: z.string().optional().nullable(), + account: z.string().optional(), + code: z.string().optional(), created_at: z.string().optional(), id: z.string().optional(), - trx_id: z.string().optional().nullable(), -}) + trx_id: z.string().optional(), +}); -export const esrRelationshipsSchema = z.tuple([]) +export const esrRelationshipsSchema = z.tuple([]); export const indexerRowSchema = z.object({ id: z.number(), last_indexed_block: z.string(), -}) +}); export const indexerInsertSchema = z.object({ id: z.number().optional(), last_indexed_block: z.string(), -}) +}); export const indexerUpdateSchema = z.object({ id: z.number().optional(), last_indexed_block: z.string().optional(), -}) +}); -export const indexerRelationshipsSchema = z.tuple([]) +export const indexerRelationshipsSchema = z.tuple([]); export const orderRowSchema = z.object({ auction_id: z.number(), @@ -153,7 +184,7 @@ export const orderRowSchema = z.object({ transactionHash: z.string(), user_id: z.number(), volume: z.number().nullable(), -}) +}); export const orderInsertSchema = z.object({ auction_id: z.number(), @@ -165,7 +196,7 @@ export const orderInsertSchema = z.object({ transactionHash: z.string(), user_id: z.number(), volume: z.number().optional().nullable(), -}) +}); export const orderUpdateSchema = z.object({ auction_id: z.number().optional(), @@ -177,61 +208,142 @@ export const orderUpdateSchema = z.object({ transactionHash: z.string().optional(), user_id: z.number().optional(), volume: z.number().optional().nullable(), -}) +}); -export const orderRelationshipsSchema = z.tuple([]) +export const orderRelationshipsSchema = z.tuple([]); export const presaleRowSchema = z.object({ - account: z.string().nullable(), - address: z.string().nullable(), + account: z.string(), + address: z.string(), + close_timestampz: z.string().nullable(), created_at: z.string(), + end_timestamptz: z.string(), + fundraising_goal: z.number(), id: z.number(), - project_id: z.number().nullable(), - signature: z.string().nullable(), -}) + max_allocation: z.number(), + project_id: z.number(), + start_timestamptz: z.string(), + total_raised: z.number(), +}); export const presaleInsertSchema = z.object({ - account: z.string().optional().nullable(), - address: z.string().optional().nullable(), + account: z.string(), + address: z.string(), + close_timestampz: z.string().optional().nullable(), created_at: z.string().optional(), + end_timestamptz: z.string(), + fundraising_goal: z.number(), id: z.number().optional(), - project_id: z.number().optional().nullable(), - signature: z.string().optional().nullable(), -}) + max_allocation: z.number(), + project_id: z.number(), + start_timestamptz: z.string(), + total_raised: z.number().optional(), +}); export const presaleUpdateSchema = z.object({ - account: z.string().optional().nullable(), - address: z.string().optional().nullable(), + account: z.string().optional(), + address: z.string().optional(), + close_timestampz: z.string().optional().nullable(), created_at: z.string().optional(), + end_timestamptz: z.string().optional(), + fundraising_goal: z.number().optional(), id: z.number().optional(), - project_id: z.number().optional().nullable(), - signature: z.string().optional().nullable(), -}) + max_allocation: z.number().optional(), + project_id: z.number().optional(), + start_timestamptz: z.string().optional(), + total_raised: z.number().optional(), +}); + +export const presaleRelationshipsSchema = z.tuple([ + z.object({ + foreignKeyName: z.literal("presale_project_id_fkey"), + columns: z.tuple([z.literal("project_id")]), + isOneToOne: z.literal(false), + referencedRelation: z.literal("project"), + referencedColumns: z.tuple([z.literal("id")]), + }), +]); + +export const presaleDepositRowSchema = z.object({ + amount: z.number(), + created_at: z.string(), + deposit_hash: z.string(), + id: z.string(), + issuance_hash: z.string().nullable(), + presale_id: z.number(), +}); -export const presaleRelationshipsSchema = z.tuple([]) +export const presaleDepositInsertSchema = z.object({ + amount: z.number(), + created_at: z.string().optional(), + deposit_hash: z.string(), + id: z.string().optional(), + issuance_hash: z.string().optional().nullable(), + presale_id: z.number(), +}); + +export const presaleDepositUpdateSchema = z.object({ + amount: z.number().optional(), + created_at: z.string().optional(), + deposit_hash: z.string().optional(), + id: z.string().optional(), + issuance_hash: z.string().optional().nullable(), + presale_id: z.number().optional(), +}); + +export const presaleDepositRelationshipsSchema = z.tuple([ + z.object({ + foreignKeyName: z.literal("presale_deposit_deposit_hash_fkey"), + columns: z.tuple([z.literal("deposit_hash")]), + isOneToOne: z.literal(false), + referencedRelation: z.literal("transaction"), + referencedColumns: z.tuple([z.literal("hash")]), + }), + z.object({ + foreignKeyName: z.literal("presale_deposit_issuance_hash_fkey"), + columns: z.tuple([z.literal("issuance_hash")]), + isOneToOne: z.literal(false), + referencedRelation: z.literal("transaction"), + referencedColumns: z.tuple([z.literal("hash")]), + }), + z.object({ + foreignKeyName: z.literal("presale_deposit_presale_id_fkey"), + columns: z.tuple([z.literal("presale_id")]), + isOneToOne: z.literal(false), + referencedRelation: z.literal("presale"), + referencedColumns: z.tuple([z.literal("id")]), + }), + z.object({ + foreignKeyName: z.literal("presale_deposits_presale_id_fkey"), + columns: z.tuple([z.literal("presale_id")]), + isOneToOne: z.literal(false), + referencedRelation: z.literal("presale"), + referencedColumns: z.tuple([z.literal("id")]), + }), +]); export const projectRowSchema = z.object({ created_at: z.string(), id: z.number(), - name: z.string().nullable(), - pitch: z.string().nullable(), -}) + name: z.string(), + pitch: z.string(), +}); export const projectInsertSchema = z.object({ created_at: z.string().optional(), id: z.number().optional(), - name: z.string().optional().nullable(), - pitch: z.string().optional().nullable(), -}) + name: z.string(), + pitch: z.string(), +}); export const projectUpdateSchema = z.object({ created_at: z.string().optional(), id: z.number().optional(), - name: z.string().optional().nullable(), - pitch: z.string().optional().nullable(), -}) + name: z.string().optional(), + pitch: z.string().optional(), +}); -export const projectRelationshipsSchema = z.tuple([]) +export const projectRelationshipsSchema = z.tuple([]); export const sessionRowSchema = z.object({ account: z.string(), @@ -239,7 +351,7 @@ export const sessionRowSchema = z.object({ esr_code: z.string().nullable(), id: z.string(), tx: z.string(), -}) +}); export const sessionInsertSchema = z.object({ account: z.string(), @@ -247,7 +359,7 @@ export const sessionInsertSchema = z.object({ esr_code: z.string().optional().nullable(), id: z.string().optional(), tx: z.string(), -}) +}); export const sessionUpdateSchema = z.object({ account: z.string().optional(), @@ -255,96 +367,117 @@ export const sessionUpdateSchema = z.object({ esr_code: z.string().optional().nullable(), id: z.string().optional(), tx: z.string().optional(), -}) +}); + +export const sessionRelationshipsSchema = z.tuple([]); + +export const chainTypeSchema = z.union([ + z.literal("evm"), + z.literal("eos"), + z.literal("solana"), + z.literal("cosmos"), +]); + +export const trxTypeSchema = z.union([ + z.literal("presale_deposit"), + z.literal("usdcred_deposit"), + z.literal("usdcred_withdrawal"), +]); + +export const transactionInsertSchema = z.object({ + chain_id: z.number().optional().nullable(), + chain_type: chainTypeSchema.optional().nullable(), + created_at: z.string().optional(), + final: z.boolean().optional().nullable(), + hash: z.string(), + trx_type: trxTypeSchema.optional().nullable(), +}); + +export const transactionUpdateSchema = z.object({ + chain_id: z.number().optional().nullable(), + chain_type: chainTypeSchema.optional().nullable(), + created_at: z.string().optional(), + final: z.boolean().optional().nullable(), + hash: z.string().optional(), + trx_type: trxTypeSchema.optional().nullable(), +}); -export const sessionRelationshipsSchema = z.tuple([]) +export const transactionRelationshipsSchema = z.tuple([]); export const transferRowSchema = z.object({ amount: z.number().nullable(), bl_presale_trx: z.string().nullable(), - chain_id: z.number().nullable(), - created_at: z.string(), from: z.string().nullable(), to: z.string().nullable(), token: z.string().nullable(), trx_hash: z.string(), type: z.string().nullable(), usdcred_trx: z.string().nullable(), -}) +}); export const transferInsertSchema = z.object({ amount: z.number().optional().nullable(), bl_presale_trx: z.string().optional().nullable(), - chain_id: z.number().optional().nullable(), - created_at: z.string().optional(), from: z.string().optional().nullable(), to: z.string().optional().nullable(), token: z.string().optional().nullable(), trx_hash: z.string(), type: z.string().optional().nullable(), usdcred_trx: z.string().optional().nullable(), -}) +}); export const transferUpdateSchema = z.object({ amount: z.number().optional().nullable(), bl_presale_trx: z.string().optional().nullable(), - chain_id: z.number().optional().nullable(), - created_at: z.string().optional(), from: z.string().optional().nullable(), to: z.string().optional().nullable(), token: z.string().optional().nullable(), trx_hash: z.string().optional(), type: z.string().optional().nullable(), usdcred_trx: z.string().optional().nullable(), -}) - -export const transferRelationshipsSchema = z.tuple([]) +}); -export const userRowSchema = z.object({ - account: z.string(), - address: z.array(z.string()), - created_at: z.string(), - id: z.number(), - short_link: z.string().nullable(), -}) - -export const userInsertSchema = z.object({ - account: z.string(), - address: z.array(z.string()), - created_at: z.string().optional(), - id: z.number().optional(), - short_link: z.string().optional().nullable(), -}) - -export const userUpdateSchema = z.object({ - account: z.string().optional(), - address: z.array(z.string()).optional(), - created_at: z.string().optional(), - id: z.number().optional(), - short_link: z.string().optional().nullable(), -}) - -export const userRelationshipsSchema = z.tuple([]) +export const transferRelationshipsSchema = z.tuple([]); export const whitelistRowSchema = z.object({ account: z.string(), address: z.string(), created_at: z.string(), project_id: z.number(), -}) + signed_message: z.string(), +}); export const whitelistInsertSchema = z.object({ account: z.string(), address: z.string(), created_at: z.string().optional(), project_id: z.number(), -}) + signed_message: z.string(), +}); export const whitelistUpdateSchema = z.object({ account: z.string().optional(), address: z.string().optional(), created_at: z.string().optional(), project_id: z.number().optional(), -}) - -export const whitelistRelationshipsSchema = z.tuple([]) + signed_message: z.string().optional(), +}); + +export const whitelistRelationshipsSchema = z.tuple([ + z.object({ + foreignKeyName: z.literal("whitelist_project_id_fkey"), + columns: z.tuple([z.literal("project_id")]), + isOneToOne: z.literal(false), + referencedRelation: z.literal("project"), + referencedColumns: z.tuple([z.literal("id")]), + }), +]); + +export const transactionRowSchema = z.object({ + chain_id: z.number().nullable(), + chain_type: chainTypeSchema.nullable(), + created_at: z.string(), + final: z.boolean().nullable(), + hash: z.string(), + trx_type: trxTypeSchema.nullable(), +}); diff --git a/apps/supabase/src/supa.types.ts b/packages/supabase/src/supa.types.ts similarity index 62% rename from apps/supabase/src/supa.types.ts rename to packages/supabase/src/supa.types.ts index e5ed15432..406e0cb9d 100644 --- a/apps/supabase/src/supa.types.ts +++ b/packages/supabase/src/supa.types.ts @@ -9,6 +9,24 @@ export type Json = export type Database = { public: { Tables: { + account: { + Row: { + account: string + created_at: string + short_link: string | null + } + Insert: { + account: string + created_at?: string + short_link?: string | null + } + Update: { + account?: string + created_at?: string + short_link?: string | null + } + Relationships: [] + } auction: { Row: { address_auctioning_token: string | null @@ -32,6 +50,7 @@ export type Database = { min_funding_threshold: number | null minimum_bidding_amount_per_order: number | null order_cancellation_end_date: string | null + project_id: number | null starting_time_stamp: string | null symbol_auctioning_token: string | null symbol_bidding_token: string | null @@ -59,6 +78,7 @@ export type Database = { min_funding_threshold?: number | null minimum_bidding_amount_per_order?: number | null order_cancellation_end_date?: string | null + project_id?: number | null starting_time_stamp?: string | null symbol_auctioning_token?: string | null symbol_bidding_token?: string | null @@ -86,34 +106,43 @@ export type Database = { min_funding_threshold?: number | null minimum_bidding_amount_per_order?: number | null order_cancellation_end_date?: string | null + project_id?: number | null starting_time_stamp?: string | null symbol_auctioning_token?: string | null symbol_bidding_token?: string | null usd_amount_traded?: number | null } - Relationships: [] + Relationships: [ + { + foreignKeyName: "auction_project_id_fkey" + columns: ["project_id"] + isOneToOne: false + referencedRelation: "project" + referencedColumns: ["id"] + }, + ] } esr: { Row: { - account: string | null - code: string | null + account: string + code: string created_at: string id: string - trx_id: string | null + trx_id: string } Insert: { - account?: string | null - code?: string | null + account: string + code: string created_at?: string id?: string - trx_id?: string | null + trx_id: string } Update: { - account?: string | null - code?: string | null + account?: string + code?: string created_at?: string id?: string - trx_id?: string | null + trx_id?: string } Relationships: [] } @@ -170,49 +199,128 @@ export type Database = { } presale: { Row: { - account: string | null - address: string | null + account: string + address: string + close_timestampz: string | null created_at: string + end_timestamptz: string + fundraising_goal: number id: number - project_id: number | null - signature: string | null + max_allocation: number + project_id: number + start_timestamptz: string + total_raised: number } Insert: { - account?: string | null - address?: string | null + account: string + address: string + close_timestampz?: string | null created_at?: string + end_timestamptz: string + fundraising_goal: number id?: number - project_id?: number | null - signature?: string | null + max_allocation: number + project_id: number + start_timestamptz: string + total_raised?: number } Update: { - account?: string | null - address?: string | null + account?: string + address?: string + close_timestampz?: string | null created_at?: string + end_timestamptz?: string + fundraising_goal?: number id?: number - project_id?: number | null - signature?: string | null + max_allocation?: number + project_id?: number + start_timestamptz?: string + total_raised?: number } - Relationships: [] + Relationships: [ + { + foreignKeyName: "presale_project_id_fkey" + columns: ["project_id"] + isOneToOne: false + referencedRelation: "project" + referencedColumns: ["id"] + }, + ] + } + presale_deposit: { + Row: { + amount: number + created_at: string + deposit_hash: string + id: string + issuance_hash: string | null + presale_id: number + } + Insert: { + amount: number + created_at?: string + deposit_hash: string + id?: string + issuance_hash?: string | null + presale_id: number + } + Update: { + amount?: number + created_at?: string + deposit_hash?: string + id?: string + issuance_hash?: string | null + presale_id?: number + } + Relationships: [ + { + foreignKeyName: "presale_deposit_deposit_hash_fkey" + columns: ["deposit_hash"] + isOneToOne: false + referencedRelation: "transaction" + referencedColumns: ["hash"] + }, + { + foreignKeyName: "presale_deposit_issuance_hash_fkey" + columns: ["issuance_hash"] + isOneToOne: false + referencedRelation: "transaction" + referencedColumns: ["hash"] + }, + { + foreignKeyName: "presale_deposit_presale_id_fkey" + columns: ["presale_id"] + isOneToOne: false + referencedRelation: "presale" + referencedColumns: ["id"] + }, + { + foreignKeyName: "presale_deposits_presale_id_fkey" + columns: ["presale_id"] + isOneToOne: false + referencedRelation: "presale" + referencedColumns: ["id"] + }, + ] } project: { Row: { created_at: string id: number - name: string | null - pitch: string | null + name: string + pitch: string } Insert: { created_at?: string id?: number - name?: string | null - pitch?: string | null + name: string + pitch: string } Update: { created_at?: string id?: number - name?: string | null - pitch?: string | null + name?: string + pitch?: string } Relationships: [] } @@ -240,12 +348,37 @@ export type Database = { } Relationships: [] } + transaction: { + Row: { + chain_id: number | null + chain_type: Database["public"]["Enums"]["chain_type"] | null + created_at: string + final: boolean | null + hash: string + trx_type: Database["public"]["Enums"]["trx_type"] | null + } + Insert: { + chain_id?: number | null + chain_type?: Database["public"]["Enums"]["chain_type"] | null + created_at?: string + final?: boolean | null + hash: string + trx_type?: Database["public"]["Enums"]["trx_type"] | null + } + Update: { + chain_id?: number | null + chain_type?: Database["public"]["Enums"]["chain_type"] | null + created_at?: string + final?: boolean | null + hash?: string + trx_type?: Database["public"]["Enums"]["trx_type"] | null + } + Relationships: [] + } transfer: { Row: { amount: number | null bl_presale_trx: string | null - chain_id: number | null - created_at: string from: string | null to: string | null token: string | null @@ -256,8 +389,6 @@ export type Database = { Insert: { amount?: number | null bl_presale_trx?: string | null - chain_id?: number | null - created_at?: string from?: string | null to?: string | null token?: string | null @@ -268,8 +399,6 @@ export type Database = { Update: { amount?: number | null bl_presale_trx?: string | null - chain_id?: number | null - created_at?: string from?: string | null to?: string | null token?: string | null @@ -279,50 +408,37 @@ export type Database = { } Relationships: [] } - user: { - Row: { - account: string - address: string[] - created_at: string - id: number - short_link: string | null - } - Insert: { - account: string - address: string[] - created_at?: string - id?: number - short_link?: string | null - } - Update: { - account?: string - address?: string[] - created_at?: string - id?: number - short_link?: string | null - } - Relationships: [] - } whitelist: { Row: { account: string address: string created_at: string project_id: number + signed_message: string } Insert: { account: string address: string created_at?: string project_id: number + signed_message: string } Update: { account?: string address?: string created_at?: string project_id?: number + signed_message?: string } - Relationships: [] + Relationships: [ + { + foreignKeyName: "whitelist_project_id_fkey" + columns: ["project_id"] + isOneToOne: false + referencedRelation: "project" + referencedColumns: ["id"] + }, + ] } } Views: { @@ -332,7 +448,8 @@ export type Database = { [_ in never]: never } Enums: { - [_ in never]: never + chain_type: "evm" | "eos" | "solana" | "cosmos" + trx_type: "presale_deposit" | "usdcred_deposit" | "usdcred_withdrawal" } CompositeTypes: { [_ in never]: never @@ -340,27 +457,27 @@ export type Database = { } } -type PublicSchema = Database[Extract] +type PublicSchema = Database[Extract] export type Tables< PublicTableNameOrOptions extends - | keyof (PublicSchema['Tables'] & PublicSchema['Views']) + | keyof (PublicSchema["Tables"] & PublicSchema["Views"]) | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof (Database[PublicTableNameOrOptions['schema']]['Tables'] & - Database[PublicTableNameOrOptions['schema']]['Views']) + ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"]) : never = never, > = PublicTableNameOrOptions extends { schema: keyof Database } - ? (Database[PublicTableNameOrOptions['schema']]['Tables'] & - Database[PublicTableNameOrOptions['schema']]['Views'])[TableName] extends { + ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends { Row: infer R } ? R : never - : PublicTableNameOrOptions extends keyof (PublicSchema['Tables'] & - PublicSchema['Views']) - ? (PublicSchema['Tables'] & - PublicSchema['Views'])[PublicTableNameOrOptions] extends { + : PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] & + PublicSchema["Views"]) + ? (PublicSchema["Tables"] & + PublicSchema["Views"])[PublicTableNameOrOptions] extends { Row: infer R } ? R @@ -369,19 +486,19 @@ export type Tables< export type TablesInsert< PublicTableNameOrOptions extends - | keyof PublicSchema['Tables'] + | keyof PublicSchema["Tables"] | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] : never = never, > = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { Insert: infer I } ? I : never - : PublicTableNameOrOptions extends keyof PublicSchema['Tables'] - ? PublicSchema['Tables'][PublicTableNameOrOptions] extends { + : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] + ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { Insert: infer I } ? I @@ -390,19 +507,19 @@ export type TablesInsert< export type TablesUpdate< PublicTableNameOrOptions extends - | keyof PublicSchema['Tables'] + | keyof PublicSchema["Tables"] | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicTableNameOrOptions['schema']]['Tables'] + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] : never = never, > = PublicTableNameOrOptions extends { schema: keyof Database } - ? Database[PublicTableNameOrOptions['schema']]['Tables'][TableName] extends { + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { Update: infer U } ? U : never - : PublicTableNameOrOptions extends keyof PublicSchema['Tables'] - ? PublicSchema['Tables'][PublicTableNameOrOptions] extends { + : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] + ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { Update: infer U } ? U @@ -411,13 +528,13 @@ export type TablesUpdate< export type Enums< PublicEnumNameOrOptions extends - | keyof PublicSchema['Enums'] + | keyof PublicSchema["Enums"] | { schema: keyof Database }, EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } - ? keyof Database[PublicEnumNameOrOptions['schema']]['Enums'] + ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"] : never = never, > = PublicEnumNameOrOptions extends { schema: keyof Database } - ? Database[PublicEnumNameOrOptions['schema']]['Enums'][EnumName] - : PublicEnumNameOrOptions extends keyof PublicSchema['Enums'] - ? PublicSchema['Enums'][PublicEnumNameOrOptions] + ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName] + : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"] + ? PublicSchema["Enums"][PublicEnumNameOrOptions] : never diff --git a/packages/tokens/README.md b/packages/tokens/README.md new file mode 100644 index 000000000..bf7bdce79 --- /dev/null +++ b/packages/tokens/README.md @@ -0,0 +1,15 @@ +# @repo/tokens + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run src/index.ts +``` + +This project was created using `bun init` in bun v1.1.24. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/packages/tokens/package.json b/packages/tokens/package.json new file mode 100644 index 000000000..0c01c51c7 --- /dev/null +++ b/packages/tokens/package.json @@ -0,0 +1,10 @@ +{ + "name": "@repo/tokens", + "main": "src/index.ts", + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } +} diff --git a/packages/tokens/src/eos.ts b/packages/tokens/src/eos.ts new file mode 100644 index 000000000..6ece8fe8a --- /dev/null +++ b/packages/tokens/src/eos.ts @@ -0,0 +1,22 @@ +import type { AntelopeToken } from './types' + +export const antelopeTokens: AntelopeToken[] = [ + { + address: 'bkbtokentest', + chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906', + symbol: 'BITUSD', + chainType: 'antelope', + chainName: 'EOS', + decimals: 4, + isStable: true, + }, + { + address: 'tethertether', + chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906', + symbol: 'USDT', + chainType: 'antelope', + chainName: 'EOS', + decimals: 4, + isStable: true, + }, +] diff --git a/packages/tokens/src/evm.ts b/packages/tokens/src/evm.ts new file mode 100644 index 000000000..538789c9b --- /dev/null +++ b/packages/tokens/src/evm.ts @@ -0,0 +1,96 @@ +import type { EVMToken } from './types' + +const baseTokens = [ + { + address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + chainId: 1, + symbol: 'USDT', + chainName: 'Ethereum', + }, + { + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + chainId: 1, + symbol: 'USDC', + chainName: 'Ethereum', + }, + { + address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', + chainId: 137, + symbol: 'USDT', + chainName: 'Polygon', + }, + { + address: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', + chainId: 137, + symbol: 'USDC', + chainName: 'Polygon', + }, + { + address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', + chainId: 42161, + symbol: 'USDT', + chainName: 'Arbitrum', + }, + { + address: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', + chainId: 42161, + symbol: 'USDC', + chainName: 'Arbitrum', + }, + { + address: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', + chainId: 10, + symbol: 'USDT', + chainName: 'Optimism', + }, + { + address: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', + chainId: 10, + symbol: 'USDC', + chainName: 'Optimism', + }, + { + address: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', + chainId: 8453, + symbol: 'USDT', + chainName: 'Base', + }, + { + address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', + chainId: 8453, + symbol: 'USDC', + chainName: 'Base', + }, + { + address: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', + chainId: 43114, + symbol: 'USDT', + chainName: 'Avalanche', + }, + { + address: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', + chainId: 43114, + symbol: 'USDC', + chainName: 'Avalanche', + }, + { + address: '0x55d398326f99059fF775485246999027B3197955', + chainId: 56, + symbol: 'USDT', + chainName: 'BNB Chain', + }, + { + address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', + chainId: 56, + symbol: 'USDC', + chainName: 'BNB Chain', + }, +] + +export const evmTokens: EVMToken[] = baseTokens.map((token) => ({ + ...token, + chainType: 'evm', + // On BNB Chain (chainId 56), both USDT and USDC are implemented with 18 decimal places instead of the usual 6. + decimals: token.chainId === 56 ? 18 : 6, + isStable: true, +})) diff --git a/packages/tokens/src/index.ts b/packages/tokens/src/index.ts new file mode 100644 index 000000000..1fa929aed --- /dev/null +++ b/packages/tokens/src/index.ts @@ -0,0 +1,15 @@ +import type { Token } from './types' +export * from './types' +export * from './eos' +export * from './evm' +export * from './solana' + +import { antelopeTokens } from './eos' +import { evmTokens } from './evm' +import { solanaTokens } from './solana' + +export const tokens: Token[] = [ + ...antelopeTokens, + ...evmTokens, + ...solanaTokens, +] diff --git a/packages/tokens/src/solana.ts b/packages/tokens/src/solana.ts new file mode 100644 index 000000000..60bf49991 --- /dev/null +++ b/packages/tokens/src/solana.ts @@ -0,0 +1,21 @@ +import type { SolanaToken } from './types' + +const baseTokens = [ + { + symbol: 'USDC', + address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + }, + { + symbol: 'USDT', + address: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', + }, +] + +export const solanaTokens: SolanaToken[] = baseTokens.map((token) => ({ + ...token, + decimals: 6, + image: `https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/${token.address}/logo.png`, + chainName: 'Solana', + isStable: true, + chainType: 'solana' as const, +})) diff --git a/packages/tokens/src/types.ts b/packages/tokens/src/types.ts new file mode 100644 index 000000000..392663d61 --- /dev/null +++ b/packages/tokens/src/types.ts @@ -0,0 +1,34 @@ +export interface BaseToken { + symbol: string + chainName: string + image?: string + decimals: number + isStable: boolean +} + +export interface EVMToken extends BaseToken { + address: string + chainId: number + chainType: 'evm' +} + +export interface AntelopeToken extends BaseToken { + address: string + chainId: string + chainType: 'antelope' +} + +export interface SolanaToken extends BaseToken { + address: string // Solana uses public key as address + chainType: 'solana' +} + +export interface CosmosToken extends BaseToken { + denom: string // Cosmos uses denom instead of address + chainId: string + chainType: 'cosmos' +} + +export type Token = EVMToken | AntelopeToken | SolanaToken | CosmosToken + +export type ChainType = 'evm' | 'antelope' | 'solana' | 'cosmos' diff --git a/packages/tokens/tsconfig.json b/packages/tokens/tsconfig.json new file mode 100644 index 000000000..7ce5ae9d8 --- /dev/null +++ b/packages/tokens/tsconfig.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Default", + "compilerOptions": { + "composite": false, + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "isolatedModules": true, + "moduleResolution": "node", + "noUnusedLocals": false, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "skipLibCheck": true, + "strict": true + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/packages/config-typescript/base.json b/packages/tsconfig/base.json similarity index 100% rename from packages/config-typescript/base.json rename to packages/tsconfig/base.json diff --git a/packages/config-typescript/nextjs-14.json b/packages/tsconfig/nextjs-14.json similarity index 100% rename from packages/config-typescript/nextjs-14.json rename to packages/tsconfig/nextjs-14.json diff --git a/packages/config-typescript/nextjs.json b/packages/tsconfig/nextjs.json similarity index 100% rename from packages/config-typescript/nextjs.json rename to packages/tsconfig/nextjs.json diff --git a/packages/config-typescript/node16.json b/packages/tsconfig/node16.json similarity index 100% rename from packages/config-typescript/node16.json rename to packages/tsconfig/node16.json diff --git a/packages/config-typescript/package.json b/packages/tsconfig/package.json similarity index 75% rename from packages/config-typescript/package.json rename to packages/tsconfig/package.json index 27c0e6043..58907bf2f 100644 --- a/packages/config-typescript/package.json +++ b/packages/tsconfig/package.json @@ -1,5 +1,5 @@ { - "name": "@repo/typescript-config", + "name": "@repo/tsconfig", "version": "0.0.0", "private": true, "license": "MIT", diff --git a/packages/config-typescript/react-library.json b/packages/tsconfig/react-library.json similarity index 100% rename from packages/config-typescript/react-library.json rename to packages/tsconfig/react-library.json diff --git a/packages/config-typescript/vite.json b/packages/tsconfig/vite.json similarity index 93% rename from packages/config-typescript/vite.json rename to packages/tsconfig/vite.json index 7c67aeed4..d616cc738 100644 --- a/packages/config-typescript/vite.json +++ b/packages/tsconfig/vite.json @@ -35,7 +35,7 @@ ], "references": [ { - "path": "./@repo/typescript-config.node.json" + "path": "./@repo/tsconfig.node.json" } ] } \ No newline at end of file diff --git a/packages/config-typescript/vite.node.json b/packages/tsconfig/vite.node.json similarity index 100% rename from packages/config-typescript/vite.node.json rename to packages/tsconfig/vite.node.json diff --git a/packages/app-lib/package.json b/packages/utils/package.json similarity index 85% rename from packages/app-lib/package.json rename to packages/utils/package.json index 3a86138fe..d2a970e23 100644 --- a/packages/app-lib/package.json +++ b/packages/utils/package.json @@ -1,5 +1,5 @@ { - "name": "app-lib", + "name": "@repo/utils", "private": true, "version": "0.0.0", "main": "./src/index.ts", @@ -15,7 +15,7 @@ "devDependencies": { "@types/jsonwebtoken": "^9.0.5", "@types/lodash": "^4.14.195", - "@repo/typescript-config": "workspace:*", + "@repo/tsconfig": "workspace:*", "typescript": "^5.3.3" } } diff --git a/packages/app-lib/src/crypto/crypto.lib.ts b/packages/utils/src/crypto/crypto.lib.ts similarity index 100% rename from packages/app-lib/src/crypto/crypto.lib.ts rename to packages/utils/src/crypto/crypto.lib.ts diff --git a/packages/app-lib/src/crypto/index.ts b/packages/utils/src/crypto/index.ts similarity index 100% rename from packages/app-lib/src/crypto/index.ts rename to packages/utils/src/crypto/index.ts diff --git a/packages/app-lib/src/date/date.lib.ts b/packages/utils/src/date/date.lib.ts similarity index 100% rename from packages/app-lib/src/date/date.lib.ts rename to packages/utils/src/date/date.lib.ts diff --git a/packages/app-lib/src/date/index.ts b/packages/utils/src/date/index.ts similarity index 100% rename from packages/app-lib/src/date/index.ts rename to packages/utils/src/date/index.ts diff --git a/packages/app-lib/src/error/error.lib.ts b/packages/utils/src/error/error.lib.ts similarity index 100% rename from packages/app-lib/src/error/error.lib.ts rename to packages/utils/src/error/error.lib.ts diff --git a/packages/app-lib/src/error/index.ts b/packages/utils/src/error/index.ts similarity index 100% rename from packages/app-lib/src/error/index.ts rename to packages/utils/src/error/index.ts diff --git a/packages/app-lib/src/evm/evm.lib.ts b/packages/utils/src/evm/evm.lib.ts similarity index 100% rename from packages/app-lib/src/evm/evm.lib.ts rename to packages/utils/src/evm/evm.lib.ts diff --git a/packages/app-lib/src/evm/index.ts b/packages/utils/src/evm/index.ts similarity index 100% rename from packages/app-lib/src/evm/index.ts rename to packages/utils/src/evm/index.ts diff --git a/packages/app-lib/src/index.ts b/packages/utils/src/index.ts similarity index 100% rename from packages/app-lib/src/index.ts rename to packages/utils/src/index.ts diff --git a/packages/app-lib/src/number/index.ts b/packages/utils/src/number/index.ts similarity index 100% rename from packages/app-lib/src/number/index.ts rename to packages/utils/src/number/index.ts diff --git a/packages/app-lib/src/number/number.lib.ts b/packages/utils/src/number/number.lib.ts similarity index 100% rename from packages/app-lib/src/number/number.lib.ts rename to packages/utils/src/number/number.lib.ts diff --git a/packages/app-lib/src/object/index.ts b/packages/utils/src/object/index.ts similarity index 100% rename from packages/app-lib/src/object/index.ts rename to packages/utils/src/object/index.ts diff --git a/packages/app-lib/src/object/object.lib.ts b/packages/utils/src/object/object.lib.ts similarity index 100% rename from packages/app-lib/src/object/object.lib.ts rename to packages/utils/src/object/object.lib.ts diff --git a/packages/app-lib/src/runtime/index.ts b/packages/utils/src/runtime/index.ts similarity index 100% rename from packages/app-lib/src/runtime/index.ts rename to packages/utils/src/runtime/index.ts diff --git a/packages/app-lib/src/runtime/runtime.lib.ts b/packages/utils/src/runtime/runtime.lib.ts similarity index 100% rename from packages/app-lib/src/runtime/runtime.lib.ts rename to packages/utils/src/runtime/runtime.lib.ts diff --git a/packages/app-lib/src/string/index.ts b/packages/utils/src/string/index.ts similarity index 100% rename from packages/app-lib/src/string/index.ts rename to packages/utils/src/string/index.ts diff --git a/packages/app-lib/src/string/string.lib.ts b/packages/utils/src/string/string.lib.ts similarity index 100% rename from packages/app-lib/src/string/string.lib.ts rename to packages/utils/src/string/string.lib.ts diff --git a/packages/app-lib/src/url/index.ts b/packages/utils/src/url/index.ts similarity index 100% rename from packages/app-lib/src/url/index.ts rename to packages/utils/src/url/index.ts diff --git a/packages/app-lib/src/url/url.lib.ts b/packages/utils/src/url/url.lib.ts similarity index 100% rename from packages/app-lib/src/url/url.lib.ts rename to packages/utils/src/url/url.lib.ts diff --git a/packages/app-lib/tsconfig.json b/packages/utils/tsconfig.json similarity index 61% rename from packages/app-lib/tsconfig.json rename to packages/utils/tsconfig.json index d78879cf6..467a965e8 100644 --- a/packages/app-lib/tsconfig.json +++ b/packages/utils/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@repo/typescript-config/react-library.json", + "extends": "../../packages/tsconfig/react-library.json", "include": ["src", "../../apps/indexer/src/lib/logger.ts"], "exclude": ["node_modules"] }