├── .dockerignore ├── .gitattributes ├── .gitignore ├── .gitmodules ├── 4naly3er-report.md ├── AUTHORS.md ├── Dockerfile.graphql ├── Dockerfile.ingestor ├── FAQ.md ├── HACKING.md ├── LICENSE ├── Makefile ├── NOTICE.txt ├── README-sponsor.md ├── README.md ├── SECURITY ├── audits ├── README.md └── index.html ├── cmd ├── README.md ├── build.mk ├── faucet.superposition │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── deploy-lambda.sh │ ├── gqlgen.yml │ ├── graph │ │ ├── filter.go │ │ ├── filter_test.go │ │ ├── generated.go │ │ ├── model │ │ │ └── models_gen.go │ │ ├── resolver.go │ │ ├── schema.resolvers.go │ │ ├── stakers.go │ │ └── verify-turnstile.go │ ├── lib │ │ └── faucet │ │ │ ├── faucet.go │ │ │ ├── ifaucet.json │ │ │ └── request.go │ ├── main.go │ ├── schema.graphqls │ └── tools.go ├── golang.mk ├── graphql.ethereum │ ├── .dockerignore │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── deploy-lambda.sh │ ├── gqlgen.yml │ ├── graph │ │ ├── consts.go │ │ ├── generated.go │ │ ├── math.go │ │ ├── mocked.go │ │ ├── model │ │ │ ├── amount.go │ │ │ ├── liquidity-campaign.go │ │ │ ├── liquidity.go │ │ │ ├── models_gen.go │ │ │ ├── pagination.go │ │ │ ├── pool-config.go │ │ │ ├── price.go │ │ │ ├── price_test.go │ │ │ ├── seawater.go │ │ │ ├── swaps.go │ │ │ ├── token.go │ │ │ └── wallet.go │ │ ├── resolver.go │ │ └── schema.resolvers.go │ ├── lib │ │ └── erc20 │ │ │ ├── erc20.go │ │ │ ├── erc20.json │ │ │ ├── erc20_test.go │ │ │ └── multicall.json │ ├── main.go │ ├── pools.go │ ├── schema.graphqls │ └── tools.go ├── ingestor.logs.ethereum │ ├── .dockerignore │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── func.go │ ├── func_test.go │ ├── main.go │ ├── polling-db.go │ └── reflect.go └── snapshot.ethereum │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── database.go │ ├── main.go │ ├── rpc.go │ └── rpc_test.go ├── codecov.yml ├── config ├── README.md └── pools.toml ├── db ├── .gitignore ├── Dockerfile ├── Dockerfile.migrator ├── README.md ├── create.sh ├── docker.sh ├── init-database.sh ├── migrate.sh ├── migrations │ ├── 1712766721-events_seawater.sql │ ├── 1712766722-events_erc20.sql │ ├── 1713545565-seawater_positions.sql │ ├── 1714115853-checkpoint_seawater_volume_timeseries.sql │ ├── 1715975362-ingestor_checkpointing.sql │ ├── 1716273125-seawater_swaps.sql │ ├── 1718055130-faucet_init.sql.sql │ ├── 1718510127-snapshot_initial.sql │ ├── 1718663667-snapshot_liquidity_sums.sql │ ├── 1718793094-snapshot_latest_decimals.sql │ ├── 1718795061-snapshot_latest_decimals_per_wallet.sql │ ├── 1718799061-swaps_decimals_group.sql.sql │ ├── 1719380221-faucet_buffer_instead.sql │ ├── 1720264790-latest_ticks_1.sql │ ├── 1720277523-erc20_cache.sql │ ├── 1720397832-seawater_liquidity_groups_2.sql │ ├── 1720500410-seawater_swaps_2.sql │ ├── 1720503733-seawater_final_ticks_daily_2.sql │ ├── 1720504185-seawater_final_ticks_monthly_2.sql │ ├── 1720505013-seawater_latest_ticks_2.sql │ ├── 1720506347-seawater_positions_2.sql │ ├── 1723013702-events_thirdweb.sql │ ├── 1723190765-seawater_cron_some_snapshot_functions.sql │ ├── 1723693657-events_leo.sql │ ├── 1723799478-events_leo_updated.sql │ ├── 1724063832-seawater_swaps_tx.sql │ ├── 1724121172-seawater_final_ticks_monthly_3.sql │ ├── 1724121175-seawater_cron_snapshot_functions.sql │ ├── 1724128480-seawater_final_ticks_daily_3.sql │ └── 1724128481-seawater_cron_fix_all_functions.sql └── update_and_migrate.sh ├── discord-export ├── Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html ├── Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files │ ├── 021eb22c1e54e7d6b7976608d2a09da4-3028E.png │ ├── 045bf49f3bb3ecd3ddfc009a6af823ba-EF9F6.png │ ├── 059d0fe5cf95abe78825804be97230dc-0FE6E.png │ ├── 1-B2132.png │ ├── 1f389-5C738.svg │ ├── 1f3c1-445DC.svg │ ├── 1f43a-EB486.svg │ ├── 1f440-6C64D.svg │ ├── 1f44b-8A059.svg │ ├── 1f44d-27259.svg │ ├── 1f451-B565E.svg │ ├── 1f4b8-E3468.svg │ ├── 1f4c6-44E30.svg │ ├── 1f50d-195C0.svg │ ├── 1f525-8FE4F.svg │ ├── 1f596-2F736.svg │ ├── 1f642-83E8A.svg │ ├── 1f64c-7C820.svg │ ├── 1f680-A35CE.svg │ ├── 1f6a8-A8AB3.svg │ ├── 1f911-F346C.svg │ ├── 1f916-AD810.svg │ ├── 1f91e-2A114.svg │ ├── 1fa82-05AEB.svg │ ├── 1fae1-B19DE.svg │ ├── 26a0-D845B.svg │ ├── 2764-A3D25.svg │ ├── 2a9faff195fe333526cfe6ae6fce1420-49B98.png │ ├── 34d2d7de2893f18e31aa4da767ee758f-58D64.png │ ├── 4da13429705aea77fddae1baedc0844a-B2E15.png │ ├── 4de8e7cb6dfbbe8795697f1df8d66439-044AF.png │ ├── 4eaf99d01e4b5042454b4a6a8809687a-E5BB7.png │ ├── 522f23dff5218659cb3b5477db3a097c-4BA21.png │ ├── 546f74664414743e95af6c3045a13083-799B6.png │ ├── 5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png │ ├── 67594ee4b4d1fc03bca468327a0d145b-BD76A.png │ ├── 6eef2e8f35be0adcb2bd829d1a16f2a2-F341B.png │ ├── 851893157188599838-C23B5.png │ ├── 851893827089727568-5FD38.png │ ├── 851893827315826708-F59C0.png │ ├── 970d2e2f00cd7ef2134a1a3f21326349-404EA.png │ ├── 9ccd56ad-107b-4841-9f4e-02f8195734c9-97D78 │ ├── a2bb0691f7aada3a9f92c6ea95ad2575-BFEEB.png │ ├── a7c8cf829ec618d22e5d0052ebaa015b-25CF4.png │ ├── a_1bf1676e45a74aaa9b4a2f0fd1d9a798-D2471.gif │ ├── c894b6c4af55a46fb2d1aef0cd4cf6b0-27286.png │ ├── d059d6d6c98a3781344652bde0248969-CF180.png │ ├── e9d8cf0d70dd56df7330dd84eb234b69-0CF9D.png │ ├── ggsans-italic-400-E988B.woff2 │ ├── ggsans-italic-500-0777F.woff2 │ ├── ggsans-italic-600-CB411.woff2 │ ├── ggsans-italic-700-891AC.woff2 │ ├── ggsans-italic-800-D36B0.woff2 │ ├── ggsans-normal-400-1456D.woff2 │ ├── ggsans-normal-500-89CE5.woff2 │ ├── ggsans-normal-600-C1EA8.woff2 │ ├── ggsans-normal-700-1949A.woff2 │ ├── ggsans-normal-800-58487.woff2 │ ├── highlight.min-D8D27.js │ ├── image-761DC.png │ ├── image-928BE.png │ ├── image-BDEF9.png │ ├── lottie.min-99657.js │ └── solarized-dark.min-BA98F.css ├── Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt └── Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files │ ├── 9ccd56ad-107b-4841-9f4e-02f8195734c9-97D78 │ ├── image-761DC.png │ ├── image-928BE.png │ └── image-BDEF9.png ├── features └── features.json ├── go.mod ├── go.sum ├── lib ├── config │ ├── config.go │ ├── defaults.go │ └── pools.go ├── events │ ├── erc20 │ │ ├── abi.json │ │ ├── erc20.go │ │ └── types.go │ ├── events.go │ ├── leo │ │ ├── abi.json │ │ ├── leo.go │ │ ├── leo_test.go │ │ └── types.go │ ├── seawater │ │ ├── abi.json │ │ ├── seawater.go │ │ ├── seawater_test.go │ │ └── types.go │ └── thirdweb │ │ ├── abi.json │ │ ├── thirdweb.go │ │ └── types.go ├── features │ ├── features.go │ ├── features_test.go │ └── list.go ├── heartbeat │ └── heartbeat.go ├── math │ ├── concentrated-liq.go │ ├── concentrated-liq_test.go │ ├── decimals.go │ └── decimals_test.go ├── setup │ └── setup.go └── types │ ├── erc20 │ └── erc20.go │ ├── seawater │ ├── classifications.go │ └── seawater.go │ └── types.go ├── out_of_scope.txt ├── pkg ├── .cargo │ └── config.toml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── Makefile ├── README.md ├── SEAWATER_TUTORIAL.md ├── book.toml ├── config.mk ├── deploy-seawater.sh ├── deploy-solidity.sh ├── deploy.sh ├── foundry.toml ├── koko │ └── README.md ├── leo │ ├── Cargo.toml │ ├── proptest-regressions │ │ └── lib.txt │ ├── src │ │ ├── calldata.rs │ │ ├── erc20.rs │ │ ├── error.rs │ │ ├── events.rs │ │ ├── host.rs │ │ ├── immutables.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── maths.rs │ │ ├── nft_manager.rs │ │ └── seawater.rs │ └── tests │ │ └── lib.rs ├── package-lock.json ├── package.json ├── rust-toolchain.toml ├── seawater │ ├── Cargo.toml │ ├── README.md │ ├── proptest-regressions │ │ └── maths │ │ │ └── sqrt_price_math.txt │ ├── src │ │ ├── erc20.rs │ │ ├── error.rs │ │ ├── eth_serde.rs │ │ ├── events.rs │ │ ├── host_erc20.rs │ │ ├── host_test_shims.rs │ │ ├── host_test_utils.rs │ │ ├── immutables.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── maths │ │ │ ├── bit_math.rs │ │ │ ├── full_math.rs │ │ │ ├── liquidity_math.rs │ │ │ ├── mod.rs │ │ │ ├── sqrt_price_math.rs │ │ │ ├── swap_math.rs │ │ │ ├── tick_bitmap.rs │ │ │ ├── tick_math.rs │ │ │ ├── unsafe_math.rs │ │ │ └── utils.rs │ │ ├── permit2_types.rs │ │ ├── pool.rs │ │ ├── position.rs │ │ ├── test_shims.rs │ │ ├── test_utils.rs │ │ ├── tick.rs │ │ ├── types.rs │ │ └── wasm_erc20.rs │ └── tests │ │ ├── lib.rs │ │ ├── pools.rs │ │ ├── reference │ │ ├── full_math.rs │ │ ├── mod.rs │ │ └── tick_math.rs │ │ ├── reference_impls.proptest-regressions │ │ └── reference_impls.rs ├── sol │ ├── Faucet.sol │ ├── IERC20.sol │ ├── IERC721Metadata.sol │ ├── IERC721TokenReceiver.sol │ ├── IFaucet.sol │ ├── ILeoEvents.sol │ ├── ISeawater.sol │ ├── ISeawaterAMM.sol │ ├── ISeawaterEvents.sol │ ├── ISeawaterExecutors.sol │ ├── ISeawaterMigrations.sol │ ├── OwnershipNFTs.sol │ └── SeawaterAMM.sol ├── test-deploy.sh ├── test │ ├── FaucetTest.bin │ ├── LightweightERC20.sol │ └── permit2.sol ├── tests.sh └── tsconfig.json ├── remappings.txt ├── scope.txt ├── static ├── embed.png └── empty ├── tools └── ethereum-selector-mine.go └── web ├── .eslintrc.json ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── Dockerfile ├── Makefile ├── README.md ├── __tests__ ├── 01_lib_math │ ├── 001_sqrt_price.test.ts │ ├── 002_delta.test.ts │ ├── 003_price_to_tick.test.ts │ └── 004_liquidity.test.ts └── 02_lib_usdFormat │ └── 001_usdFormat.test.ts ├── codegen.ts ├── components.json ├── e2e ├── app.spec.ts └── fixtures.js ├── jest.config.js ├── next.config.js ├── package.json ├── playwright.config.ts ├── playwright.teardown.js ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── icons │ ├── ICON HOLE_BLACK.svg │ ├── ICON_BLACK.png │ ├── cbux.svg │ ├── ethereum-eth-logo.svg │ ├── fUSDC.svg │ ├── token.svg │ └── usd-coin-usdc-logo.svg └── site.webmanifest ├── scripts └── clean.js ├── sentry.client.config.ts ├── sentry.edge.config.ts ├── sentry.server.config.ts ├── src ├── app │ ├── PopulateQueryCache.tsx │ ├── Provider.tsx │ ├── SuperloopPopover.tsx │ ├── SwapButton.tsx │ ├── Welcome.tsx │ ├── WelcomeGradient.tsx │ ├── _DataTable │ │ ├── DataTable.tsx │ │ └── columns.tsx │ ├── _TransactionHistoryTable │ │ ├── TransactionHistoryTable.tsx │ │ └── columns.tsx │ ├── _layout │ │ ├── ConnectWalletButton.tsx │ │ ├── DemoData.tsx │ │ ├── FaucetDropdown.tsx │ │ ├── FeatureFlagConfig.tsx │ │ ├── MobileNetworkSelection.tsx │ │ ├── NavigationMenu.tsx │ │ └── NetworkSelection.tsx │ ├── global-error.tsx │ ├── globals.css │ ├── layout.tsx │ ├── page.tsx │ ├── stake │ │ ├── AllPools.tsx │ │ ├── AllPoolsFilter.tsx │ │ ├── MyPositions.tsx │ │ ├── WelcomeModal.tsx │ │ ├── YieldBreakdownClaimedDrawer.tsx │ │ ├── YieldBreakdownClaimedModal.tsx │ │ ├── YieldBreakdownDrawer.tsx │ │ ├── YieldBreakdownModal.tsx │ │ ├── YieldOverTimeGraph │ │ │ └── index.tsx │ │ ├── _AllPoolsTable │ │ │ ├── AllPoolsTable.tsx │ │ │ └── columns.tsx │ │ ├── _MyPositionsTable │ │ │ ├── MyPositionsTable.tsx │ │ │ └── columns.tsx │ │ ├── page.tsx │ │ └── pool │ │ │ ├── add-liquidity │ │ │ └── page.tsx │ │ │ ├── confirm-liquidity │ │ │ └── page.tsx │ │ │ ├── confirm-withdraw │ │ │ └── page.tsx │ │ │ ├── create │ │ │ ├── confirm │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ └── select-prime-asset │ │ │ │ ├── _SelectPrimeAssetTable │ │ │ │ ├── SelectPrimeAssetTable.tsx │ │ │ │ └── columns.tsx │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ └── withdraw-liquidity │ │ │ └── page.tsx │ └── swap │ │ ├── confirm │ │ └── page.tsx │ │ ├── explore │ │ ├── _AllAssetsTable │ │ │ ├── AllAssetsTable.tsx │ │ │ └── columns.tsx │ │ └── page.tsx │ │ └── inventory │ │ └── page.tsx ├── assets │ ├── gifs │ │ ├── congratulations.gif │ │ ├── fail.gif │ │ ├── pending.gif │ │ ├── processing.gif │ │ ├── success.gif │ │ └── unlock.gif │ ├── icons │ │ ├── ARB.svg │ │ ├── Caret.svg │ │ ├── ETH.svg │ │ ├── SPN.svg │ │ ├── Search.svg │ │ ├── Swap.svg │ │ ├── USDC.svg │ │ ├── allow-swapping.svg │ │ ├── arrow-down-white.svg │ │ ├── arrow-down.svg │ │ ├── arrow-up-right.svg │ │ ├── circles.svg │ │ ├── cog.svg │ │ ├── disconnect.svg │ │ ├── discord.svg │ │ ├── ethereum.svg │ │ ├── gas.svg │ │ ├── grid.svg │ │ ├── hourglass.svg │ │ ├── iridescent-pickaxe-2.svg │ │ ├── iridescent-pickaxe.svg │ │ ├── iridescent-token.svg │ │ ├── legend │ │ │ ├── current-price.svg │ │ │ ├── liquidity-distribution.svg │ │ │ └── selected-range.svg │ │ ├── list.svg │ │ ├── long-tail.svg │ │ ├── padlock.svg │ │ ├── position.svg │ │ ├── pro-toggle-selected.svg │ │ ├── pro-toggle.svg │ │ ├── profile-picture.svg │ │ ├── sort.svg │ │ ├── spn-test.svg │ │ ├── success.gif │ │ ├── superposition.svg │ │ ├── token-borderless.svg │ │ ├── token-iridescent.svg │ │ └── token.svg │ └── profile-picture.png ├── components │ ├── CampaignBanner.tsx │ ├── ConfirmStake.tsx │ ├── ConfirmSwap.tsx │ ├── DurationSegmentedControl.tsx │ ├── InventoryContent │ │ ├── Address.tsx │ │ ├── DataTable.tsx │ │ ├── DisconnectButton.tsx │ │ ├── InventoryHeader.tsx │ │ ├── InventorySettings.tsx │ │ ├── MyYieldUnclaimed.tsx │ │ ├── PoolsTabContent.tsx │ │ ├── Position.tsx │ │ ├── TradeTabContent.tsx │ │ ├── columns.tsx │ │ ├── data │ │ │ ├── myPositionsData.ts │ │ │ ├── traderRewardsData.ts │ │ │ ├── transactionHistoryData.tsx │ │ │ └── yieldData.ts │ │ ├── index.tsx │ │ └── useInventorySettings.ts │ ├── InventorySheet.tsx │ ├── LiquidityRangeVisualizer.tsx │ ├── Menu │ │ ├── Menu.module.scss │ │ └── index.tsx │ ├── RewardsBreakdown │ │ └── index.tsx │ ├── Slider │ │ ├── Slider.module.scss │ │ └── index.tsx │ ├── StakeForm.tsx │ ├── SwapForm.tsx │ ├── SwapPro │ │ ├── SwapProGraph.tsx │ │ ├── SwapProGraphData.ts │ │ ├── SwapProPoolFragment.tsx │ │ └── index.tsx │ ├── TokenIcon.tsx │ ├── sequence │ │ ├── Confirm.tsx │ │ ├── EnableSpending.tsx │ │ ├── Fail.tsx │ │ └── Success.tsx │ └── ui │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── drawer.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── popover.tsx │ │ ├── segmented-control.tsx │ │ ├── select.tsx │ │ ├── sheet.tsx │ │ ├── skeleton.tsx │ │ ├── switch.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── toast.tsx │ │ ├── toaster.tsx │ │ ├── tooltip.tsx │ │ ├── typography.tsx │ │ └── use-toast.ts ├── config │ ├── arbitrumStylusTestnet.ts │ ├── graphqlEndpoint.ts │ ├── index.ts │ └── tokens.ts ├── context │ └── index.tsx ├── demoData │ ├── allPools.ts │ ├── myPositions.ts │ ├── swapExploreAssets.ts │ └── yieldOverTimeData.ts ├── gql │ ├── fragment-masking.ts │ ├── gql.ts │ ├── graphql.ts │ └── index.ts ├── hooks │ ├── useDebounce.ts │ ├── useFeatureFlag.tsx │ ├── useFeatureFlagOverride.ts │ ├── useGraphql.tsx │ ├── useMediaQuery.ts │ └── usePostions.ts ├── instrumentation.ts ├── lib │ ├── abi │ │ ├── IFaucet.ts │ │ ├── ISeawaterAMM.ts │ │ └── LightweightERC20.ts │ ├── addresses.ts │ ├── amounts.ts │ ├── math.ts │ ├── padLiquidityPool.ts │ ├── publicClient.ts │ ├── usdFormat.tsx │ └── utils.ts ├── stores │ ├── useInventorySheet.ts │ ├── useStakeStore.ts │ ├── useStakeWelcomeBackStore.ts │ ├── useSwapPro.ts │ ├── useSwapStore.ts │ └── useWelcomeStore.ts └── styles │ ├── _mixins.scss │ ├── _variables.scss │ └── globals.scss ├── tailwind.config.ts └── tsconfig.json /.dockerignore: -------------------------------------------------------------------------------- 1 | pkg/target 2 | pkg/*.wasm 3 | pkg/out 4 | pkg/*.rlib 5 | Dockerfile.* 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.png filter=lfs diff=lfs merge=lfs -text 2 | **/*.jpe?g filter=lfs diff=lfs merge=lfs -text 3 | static/* filter=lfs diff=lfs merge=lfs -text 4 | */*.pdf filter=lfs diff=lfs merge=lfs -text 5 | **/*.svg filter=lfs diff=lfs merge=lfs -text 6 | **/*.gif filter=lfs diff=lfs merge=lfs -text 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # misc 2 | .zed/ 3 | .idea/ 4 | 5 | # pkg 6 | pkg/target 7 | pkg/*.wasm 8 | pkg/out 9 | pkg/*.rlib 10 | /web/.env 11 | *.zip 12 | 13 | # IDE 14 | .vscode/ 15 | 16 | .DS_Store 17 | +Errors 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pkg/contracts/lib/forge-std"] 2 | path = pkg/contracts/lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/forge-std"] 5 | branch = v1.6.1 6 | [submodule "pkg/lib/forge-std"] 7 | path = pkg/lib/forge-std 8 | url = https://github.com/foundry-rs/forge-std 9 | [submodule "automation/nitro-testnode"] 10 | path = automation/nitro-testnode 11 | url = https://github.com/OffchainLabs/nitro-testnode 12 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | 2 | # Authors 3 | 4 | Thanks to all the people who contributed to this repo! 5 | 6 | | Name | Description | 7 | |-----------------------------------------|------------------------------------------------------------| 8 | | Aidan Kinzett (Labrys) | Implemented the UI, and the graph interaction. | 9 | | Ben Hooper (Labrys) | Supported UI development with contract insights. | 10 | | Eli Demoulin (Fluidity Labs) | Testing infrastructure, frontend and quoter. Database too. | 11 | | Elle Katerina Valentine (Fluidity Labs) | Original author of the smart contracts. | 12 | | Erin Allen (Labrys) | Project managed the first UI sprint. | 13 | | Luke Shulver (Labrys) | Project managed the second UI sprint. | 14 | | Marcelo Grebois | Implemented deployment code/infrastructure. | 15 | | Ogous Chan Ali (Fluidity Labs) | Implemented parts of Longtail frontend, and testing | 16 | -------------------------------------------------------------------------------- /Dockerfile.graphql: -------------------------------------------------------------------------------- 1 | 2 | FROM golang:alpine3.19 AS builder 3 | 4 | RUN apk add --no-cache \ 5 | openssl \ 6 | ca-certificates \ 7 | make \ 8 | bash \ 9 | curl \ 10 | gcc \ 11 | musl-dev 12 | 13 | ENV SUPERPOSITION_DIR /usr/local/src/superposition 14 | 15 | WORKDIR ${SUPERPOSITION_DIR} 16 | 17 | COPY go.mod . 18 | COPY go.sum . 19 | 20 | RUN go mod download 21 | 22 | COPY lib lib/ 23 | COPY cmd cmd/ 24 | 25 | ENV INSTALL_DIR /bin 26 | 27 | RUN sh -c 'cd cmd/graphql.ethereum && make install' 28 | 29 | FROM scratch 30 | WORKDIR /bin 31 | COPY --from=builder /bin/graphql.ethereum . 32 | CMD ["graphql.ethereum"] 33 | -------------------------------------------------------------------------------- /Dockerfile.ingestor: -------------------------------------------------------------------------------- 1 | 2 | FROM golang:alpine3.19 AS builder 3 | 4 | RUN apk add --no-cache \ 5 | openssl \ 6 | ca-certificates \ 7 | make \ 8 | bash \ 9 | curl \ 10 | gcc \ 11 | musl-dev 12 | 13 | ENV SUPERPOSITION_DIR /usr/local/src/superposition 14 | 15 | WORKDIR ${SUPERPOSITION_DIR} 16 | 17 | COPY go.mod . 18 | COPY go.sum . 19 | 20 | RUN go mod download 21 | 22 | COPY lib lib/ 23 | COPY cmd cmd/ 24 | 25 | ENV INSTALL_DIR /bin 26 | 27 | RUN sh -c 'cd cmd/ingestor.logs.ethereum && make install' 28 | 29 | FROM scratch 30 | WORKDIR /bin 31 | COPY --from=builder /bin/ingestor.logs.ethereum . 32 | CMD ["ingestor.logs.ethereum"] 33 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | 2 | # Longtail 3 | 4 | Longtail is powered by Seawater, a concentrated liquidity AMM. It lives in the 5 | `pkg/seawater` directory, with some code to dispatch into it living in `pkg/sol`. 6 | 7 | ## Developing on Longtail 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/LICENSE -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: build contract docker docker-graphql docker-ingestor 3 | 4 | all: build 5 | 6 | build: contract 7 | 8 | contract: 9 | @cd pkg && make 10 | 11 | docker: docker-graphql docker-ingestor 12 | 13 | docker-graphql: 14 | @docker build -t superposition/graphql -f Dockerfile.graphql . 15 | 16 | docker-ingestor: 17 | @docker build -t superposition/ingestor -f Dockerfile.ingestor . 18 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | 2 | # Attributions 3 | 4 | The following less-popular libraries are modified and used in this 5 | codebase: 6 | 7 | ## 0xkitsune Uniswap math library 8 | 9 | This code makes heavy use of 10 | [0xkitsune](https://twitter.com/0xkitsune)'s Rust Uniswap maths code 11 | [uniswap-v3-math](https://github.com/0xKitsune/uniswap-v3-math). 12 | 13 | A reference for our use is mostly distributed in 14 | `pkg/seawater/tests/reference/`. 15 | 16 | ## Uniswap V3 17 | 18 | Uniswap for open sourcing your ideas and code, and for popularising/inventing concentrated 19 | liquidity math. 20 | -------------------------------------------------------------------------------- /README-sponsor.md: -------------------------------------------------------------------------------- 1 | 2 | # Longtail AMM (Superposition AMM) [![codecov](https://codecov.io/gh/fluidity-money/long.so/graph/badge.svg?token=LIRGRUVdTr)](https://codecov.io/gh/fluidity-money/long.so) 3 | 4 | Longtail AMM is Arbitrum's most rewarding AMM, made possible with 5 | Stylus. Longtail AMM leverages Super Assets to reward every on-chain 6 | transaction with yield, whilst remaining the most affordable AMM with 7 | a low gas profile leveraging Stylus. 8 | 9 | Stylus is a WASM frontend to the standard EVM stack made available 10 | exclusively on Arbitrum. 11 | 12 | ## Learn more about Stylus 13 | 14 | https://arbitrum.io/stylus 15 | 16 | ## Addresses 17 | 18 | Deployment addresses are available at [DEPLOYMENT.md](DEPLOYMENT.md). 19 | 20 | Architecture information is available at [ARCHITECTURE.md](ARCHITECTURE.md). 21 | 22 | ## Contributing 23 | 24 | [HACKING.md](HACKING) 25 | 26 | ## Security 27 | 28 | [Security instructions](https://github.com/fluidity-money/long.so/blob/development/SECURITY) 29 | 30 | ## Contributors 31 | 32 | [Contributors](https://github.com/fluidity-money/long.so/blob/development/AUTHORS.md) 33 | -------------------------------------------------------------------------------- /SECURITY: -------------------------------------------------------------------------------- 1 | 2 | ## Smart contract disclosures 3 | 4 | Please contact us through our Immunefi bounty program (pending for 5 | Superposition). 6 | 7 | ## Dropboxes 8 | 9 | In the event of an urgent security vulnerability and a whitehat hack 10 | is needed, please send funds rescued to (pending). 11 | 12 | ## Urgent contacts 13 | 14 | In the event of something urgent, please DO NOT hestitate to contact 15 | Alex or Ivan via either of these methods: 16 | 17 | ### Alex/Bayge (CTO) 18 | 19 | | Contact method | Contact | 20 | |----------------|---------------------------------------------------| 21 | | Telegram | [doggish](https://t.me/doggish) | 22 | | Discord | bayge | 23 | | Email | [alex@fluidity.money](mailto:alex@fluidity.money) | 24 | 25 | ### Ivan (head of product) 26 | 27 | | Contact method | Contact | 28 | |----------------|---------------------------------------------------| 29 | | Telegram | [iNash_ISN](https://t.me/iNash_ISN) | 30 | | Discord | Ivan | ISN (🌊,💸)#8511 | 31 | | Email | [ivan@fluidity.money](mailto:ivan@fluidity.money) | 32 | -------------------------------------------------------------------------------- /audits/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Audits completed 3 | 4 | This folder contains audits completed. https://audits.long.so also hosts them from this 5 | repo. 6 | -------------------------------------------------------------------------------- /audits/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Audits | Longtail AMM 5 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /cmd/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Cmdlets 3 | 4 | Cmdlets contain executables that either: 5 | 6 | 1. Respond to messages to be included in a graph request 7 | 2. Pick up messages using Kafka and then insert them into the database 8 | 9 | ## The "func" pattern 10 | 11 | Each cmdlet includes a file named "func.go" that stores the entrypoint for the code. The 12 | state is included there as an argument for simple testing. The function inside is named 13 | "Entry". The main file should set up the state. 14 | -------------------------------------------------------------------------------- /cmd/build.mk: -------------------------------------------------------------------------------- 1 | 2 | ORG_ROOT := fluidity 3 | 4 | INSTALL_DIR := $(or ${INSTALL_DIR},/usr/local/bin) 5 | 6 | .PHONY: build watch clean install 7 | 8 | MAKEFLAGS += --no-print-directory 9 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/.gitignore: -------------------------------------------------------------------------------- 1 | faucet.superposition 2 | bootstrap 3 | bootstrap.zip 4 | stakers.json 5 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../golang.mk 3 | 4 | .PHONY: lambda 5 | 6 | lambda: bootstrap.zip 7 | 8 | bootstrap: faucet.superposition 9 | @cp faucet.superposition bootstrap 10 | 11 | bootstrap.zip: bootstrap 12 | @zip bootstrap.zip bootstrap 13 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/README.md: -------------------------------------------------------------------------------- 1 | 2 | # GraphQL Faucet interface 3 | 4 | Listens for a mutation to request tokens, and sends the SPN token for Superposition 5 | Testnet on demand. With a feature flag optionally supports gating the amount of tokens 6 | send to a list of users. Batches the sends using the contract within a 5 second window 7 | (with some extra seconds added randomly.) 8 | 9 | ## Features 10 | 11 | | Name | Description | 12 | |-----------------------|--------------------------------------------------------| 13 | | `faucet stakers only` | Sends only to the list of stakers in the stakers file. | 14 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/deploy-lambda.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eu 2 | 3 | function_name="$1" 4 | 5 | aws lambda update-function-code \ 6 | --function-name "$function_name" \ 7 | --zip-file "$zip_file" 8 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - ./*.graphqls 3 | 4 | exec: 5 | filename: graph/generated.go 6 | package: graph 7 | 8 | model: 9 | filename: graph/model/models_gen.go 10 | package: model 11 | 12 | resolver: 13 | layout: follow-schema 14 | dir: graph 15 | package: graph 16 | filename_template: "{name}.resolvers.go" 17 | # Optional: turn on to not generate template comments above resolvers 18 | # omit_template_comment: false 19 | 20 | omit_slice_element_pointers: true 21 | 22 | struct_fields_always_pointers: false 23 | 24 | resolvers_always_return_pointers: false 25 | 26 | return_pointers_in_unmarshalinput: false 27 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/graph/filter.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "regexp" 7 | 8 | "github.com/ethereum/go-ethereum/ethclient" 9 | ethCommon "github.com/ethereum/go-ethereum/common" 10 | ) 11 | 12 | // reWallet to use to validate the wallet address before continuing with verification. 13 | var reWallet = regexp.MustCompile("(0x)?[A-Z0-9a-z]{40}") 14 | 15 | func IsValidWallet(a string) bool { 16 | if !reWallet.MatchString(a) { 17 | return false 18 | } 19 | return ethCommon.IsHexAddress(a) 20 | } 21 | 22 | // IsContract test by checking the contract size with Geth to see if it's greater than 0. 23 | func IsContract(c *ethclient.Client, ctx context.Context, a ethCommon.Address) (bool, error) { 24 | b, err := c.CodeAt(ctx, a, nil) 25 | if err != nil { 26 | return false, fmt.Errorf("bad code at: %v", err) 27 | } 28 | isContract := len(b) > 0 29 | return isContract, nil 30 | } 31 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/graph/filter_test.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestIsValidWallet(t *testing.T) { 10 | ok := IsValidWallet("0x6221a9c005f6e47eb398fd867784cacfdcfff4e7") 11 | assert.Truef(t, ok, "bad wallet filtering") 12 | } 13 | 14 | func TestInvalidWallet(t *testing.T) { 15 | ok := IsValidWallet("swag") 16 | assert.Falsef(t, ok, "bad wallet filtering") 17 | } 18 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/graph/model/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | type Mutation struct { 6 | } 7 | 8 | type Query struct { 9 | } 10 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/graph/resolver.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | 6 | ethCommon "github.com/ethereum/go-ethereum/common" 7 | 8 | "github.com/fluidity-money/long.so/lib/config" 9 | "github.com/fluidity-money/long.so/lib/features" 10 | ) 11 | 12 | type ( 13 | Resolver struct { 14 | DB *gorm.DB // db used to look up any fields that are missing from a request. 15 | F features.F // features to have enabled when requested 16 | C config.C // config for connecting to the right endpoints 17 | TurnstileSecret string // Turnstile secret to use for verification 18 | } 19 | 20 | // FaucetReq to an IP address given, assuming they passed the 21 | // restrictions. 22 | FaucetReq struct { 23 | Addr ethCommon.Address 24 | IsStaker bool 25 | Resp chan error 26 | } 27 | ) 28 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/graph/stakers.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // UrlModeratorsGraph to ask whether a user is currently staking and their 12 | // point threshold 13 | const UrlModeratorsGraph = "https://moderators.fluidity.money/" 14 | 15 | // StakerCutoff to require before allowing to use the faucet 16 | const StakerCutoff = 10_000 17 | 18 | func IsUserStaker(wallet string) (bool, error) { 19 | buf := strings.NewReader(fmt.Sprintf(`{"query":"query {\n getStakingInformation(addresses: [\n \"%v\"\n ]) {\n points\n }\n}"}`, wallet)) 20 | resp, err := http.Post(UrlModeratorsGraph, "application/json", buf) 21 | if err != nil { 22 | return false, err 23 | } 24 | var data struct { 25 | Data map[string][]map[string]any `json:"data"` 26 | } 27 | if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { 28 | return false, err 29 | } 30 | stakerInfo := data.Data["getStakingInformation"] 31 | if len(stakerInfo) == 0 { 32 | return false, fmt.Errorf("nothing returned from data") 33 | } 34 | var isStaker bool 35 | staker := stakerInfo[0]["points"] 36 | s, _ := staker.(string) 37 | v, err := strconv.Atoi(s) 38 | if err != nil { 39 | return false, fmt.Errorf("bad type conversion to int: %#v: %v", staker, err) 40 | } 41 | isStaker = v > StakerCutoff 42 | return isStaker, nil 43 | } 44 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/graph/verify-turnstile.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | "net/url" 9 | ) 10 | 11 | // SiteVerifyUrl to use to check Turnstile with 12 | const SiteVerifyUrl = "https://challenges.cloudflare.com/turnstile/v0/siteverify" 13 | 14 | // VerifyTurnstile by making a request to Cloudflare, returning an error 15 | // if it happens, and false if we can't verify them upstream. 16 | func VerifyTurnstile(secret, key string) (bool, error) { 17 | resp, err := http.PostForm(SiteVerifyUrl, url.Values{ 18 | "secret": {secret}, 19 | "response": {key}, 20 | }) 21 | if err != nil { 22 | return false, err 23 | } 24 | defer resp.Body.Close() 25 | switch s := resp.StatusCode; s { 26 | case http.StatusOK, http.StatusAccepted: 27 | // Do nothing 28 | default: 29 | var buf bytes.Buffer 30 | _, _ = buf.ReadFrom(resp.Body) 31 | err = fmt.Errorf("unusual status code (resp %#v): %v", buf.String(), s) 32 | return false, err 33 | } 34 | var d struct { 35 | Success bool `json:"success"` 36 | ErrorCodes []int `json:"error_codes"` 37 | } 38 | var buf bytes.Buffer 39 | if _, err := buf.ReadFrom(resp.Body); err != nil { 40 | return false, err 41 | } 42 | buf2 := buf 43 | if err := json.NewDecoder(&buf).Decode(&d); err != nil { 44 | return false, fmt.Errorf("error decoding %#v: %v", buf2.String(), err) 45 | } 46 | if e := d.ErrorCodes; len(e) > 0 { 47 | return false, fmt.Errorf("returned error codes: %v", e) 48 | } 49 | success := d.Success 50 | return success, nil 51 | } 52 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/lib/faucet/faucet.go: -------------------------------------------------------------------------------- 1 | package faucet 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | _ "embed" 7 | 8 | "github.com/ethereum/go-ethereum" 9 | ethAbi "github.com/ethereum/go-ethereum/accounts/abi" 10 | ethAbiBind "github.com/ethereum/go-ethereum/accounts/abi/bind" 11 | ethCommon "github.com/ethereum/go-ethereum/common" 12 | ethTypes "github.com/ethereum/go-ethereum/core/types" 13 | "github.com/ethereum/go-ethereum/ethclient" 14 | ) 15 | 16 | //go:embed ifaucet.json 17 | var abiBytes []byte 18 | 19 | var abi, _ = ethAbi.JSON(bytes.NewReader(abiBytes)) 20 | 21 | type FaucetReq struct { 22 | Recipient ethCommon.Address `abi:"recipient"` 23 | IsStaker bool `abi:"isStaker"` 24 | } 25 | 26 | // SendFaucet to multiple addresses, allowing the contract to randomly 27 | // choose how much to send. 28 | func SendFaucet(ctx context.Context, c *ethclient.Client, o *ethAbiBind.TransactOpts, faucet, sender ethCommon.Address, addrs ...FaucetReq) (hash *ethTypes.Transaction, err error) { 29 | bc := ethAbiBind.NewBoundContract(faucet, abi, c, c, c) 30 | d, err := abi.Pack("sendTo", addrs) 31 | if err != nil { 32 | return nil, err 33 | } 34 | g, err := c.EstimateGas(ctx, ethereum.CallMsg{ 35 | From: sender, 36 | To: &faucet, 37 | Data: d, 38 | }) 39 | if err != nil { 40 | return nil, err 41 | } 42 | g = uint64(float64(g) * 1.25) // Lazy! 43 | o.GasLimit = uint64(float64(g) * 1.5) 44 | tx, err := bc.Transact(o, "sendTo", addrs) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return tx, nil 49 | } 50 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/lib/faucet/ifaucet.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "function", 4 | "name": "sendTo", 5 | "inputs": [ 6 | { 7 | "name": "_requests", 8 | "type": "tuple[]", 9 | "internalType": "struct IFaucet.FaucetReq[]", 10 | "components": [ 11 | { 12 | "name": "recipient", 13 | "type": "address", 14 | "internalType": "address" 15 | }, 16 | { 17 | "name": "isStaker", 18 | "type": "bool", 19 | "internalType": "isStaker" 20 | } 21 | ] 22 | } 23 | ], 24 | "outputs": [], 25 | "stateMutability": "nonpayable" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/lib/faucet/request.go: -------------------------------------------------------------------------------- 1 | package faucet 2 | 3 | import "time" 4 | 5 | type FaucetRequest struct { 6 | Addr string 7 | IpAddr string 8 | CreatedBy, UpdatedBy time.Time 9 | WasSent, IsFlyStaker bool 10 | } 11 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/schema.graphqls: -------------------------------------------------------------------------------- 1 | 2 | type Mutation { 3 | """ 4 | Request tokens, with information on the IP address and the requesting address being 5 | collected with X-Forwarded-For and tracked in the database. Sends SPN token at the 6 | expense of the faucet. 7 | """ 8 | requestTokens( 9 | """ 10 | Wallet address to use to request the tokens for. 11 | """ 12 | wallet: String!, 13 | 14 | """ 15 | Cloudflare token to request from the faucet with. 16 | """ 17 | turnstileToken: String! 18 | ): String! 19 | } 20 | 21 | type Query { 22 | """ 23 | Request a healthcheck status update, including whatever's on the internal queue. 24 | """ 25 | healthcheck: Int! 26 | } 27 | -------------------------------------------------------------------------------- /cmd/faucet.superposition/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | import _ "github.com/99designs/gqlgen" 7 | -------------------------------------------------------------------------------- /cmd/golang.mk: -------------------------------------------------------------------------------- 1 | 2 | include ../build.mk 3 | 4 | ORG_ROOT := superposition 5 | 6 | GO_BUILD := CGO_ENABLED=0 go build 7 | 8 | GO_FILES := $(shell find . -name '*.go' -or -name '*.json' -type f) 9 | 10 | CMDLET := $(shell basename ${PWD}) 11 | 12 | CONFIG_DIR := ../../config 13 | 14 | ${CMDLET}: ${EXTRA_FILES} ${GO_FILES} 15 | @${GO_BUILD} ${GO_BUILD_EXTRA_ARGS} 16 | 17 | lint: ${GO_FILES} 18 | @${GO_FMT} 19 | 20 | test: ${GO_FILES} semgrep 21 | @${GO_TEST} -cover 22 | 23 | build: ${CMDLET} 24 | 25 | install: build 26 | cp ${CMDLET} ${INSTALL_DIR}/${CMDLET} 27 | 28 | watch: 29 | @ls -1 ${GO_FILES} | entr -ns 'clear && make build' 30 | 31 | clean: 32 | @rm -f "${CMDLET}" lint test docker ${EXTRA_FILES} 33 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/.dockerignore: -------------------------------------------------------------------------------- 1 | graphql.ethereum 2 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/.gitignore: -------------------------------------------------------------------------------- 1 | graphql.ethereum 2 | *.zip 3 | bootstrap 4 | pools.toml 5 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/Makefile: -------------------------------------------------------------------------------- 1 | 2 | EXTRA_FILES := pools.toml 3 | 4 | include ../golang.mk 5 | 6 | pools.toml: ${CONFIG_DIR}/pools.toml 7 | @cp ${CONFIG_DIR}/pools.toml . 8 | 9 | .PHONY: lambda 10 | 11 | lambda: bootstrap.zip 12 | 13 | bootstrap: graphql.ethereum 14 | @cp graphql.ethereum bootstrap 15 | 16 | bootstrap.zip: bootstrap 17 | @zip bootstrap.zip bootstrap 18 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/README.md: -------------------------------------------------------------------------------- 1 | 2 | # GraphQL Ethereum interface 3 | 4 | Listens for requests, and services them using GraphQL. A backend is optionally chosen 5 | using SPN_LISTEN_BACKEND, http and lambda is accepted. 6 | 7 | ## Features 8 | 9 | | Name | Description | 10 | |--------------------------|--------------------------------------------------------| 11 | | `graphql mock demo data` | Mocks out each GraphQL endpoint, returning dummy data. | 12 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/deploy-lambda.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eu 2 | 3 | function_name="$1" 4 | 5 | aws lambda update-function-code \ 6 | --function-name "$function_name" \ 7 | --zip-file "$zip_file" 8 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/gqlgen.yml: -------------------------------------------------------------------------------- 1 | # Where are all the schema files located? globs are supported eg src/**/*.graphqls 2 | schema: 3 | - ./*.graphqls 4 | 5 | # Where should the generated server code go? 6 | exec: 7 | filename: graph/generated.go 8 | package: graph 9 | 10 | # Where should any generated models go? 11 | model: 12 | filename: graph/model/models_gen.go 13 | package: model 14 | 15 | # Where should the resolver implementations go? 16 | resolver: 17 | layout: follow-schema 18 | dir: graph 19 | package: graph 20 | filename_template: "{name}.resolvers.go" 21 | # Optional: turn on to not generate template comments above resolvers 22 | # omit_template_comment: false 23 | 24 | omit_slice_element_pointers: true 25 | 26 | struct_fields_always_pointers: false 27 | 28 | resolvers_always_return_pointers: false 29 | 30 | return_pointers_in_unmarshalinput: false 31 | 32 | autobind: 33 | - "github.com/fluidity-money/long.so/cmd/graphql.ethereum/graph/model" 34 | 35 | models: 36 | ID: 37 | model: 38 | - github.com/99designs/gqlgen/graphql.ID 39 | - github.com/99designs/gqlgen/graphql.String 40 | Int: 41 | model: 42 | - github.com/99designs/gqlgen/graphql.Int 43 | - github.com/99designs/gqlgen/graphql.Int64 44 | - github.com/99designs/gqlgen/graphql.Int32 45 | SeawaterPoolClassification: 46 | model: 47 | - github.com/fluidity-money/long.so/lib/types/seawater.Classification 48 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/consts.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | const ( 4 | PoolPositionsPageSize = 50 5 | SwapPositionsPageSize = 10 6 | ) 7 | 8 | const LiquidityGroupsLimit = 20 9 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/math.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "math/big" 4 | 5 | var FiveThousand = new(big.Int).SetInt64(500) 6 | var Ten = new(big.Int).SetInt64(10) 7 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/amount.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "github.com/fluidity-money/long.so/lib/types" 8 | ) 9 | 10 | // Amount often returned from a PairAmount, containing the price of the 11 | // asset (optionally scaled), and the timestamp when it was produced. 12 | type Amount struct { 13 | Token types.Address `json:"token"` 14 | Decimals int `json:"decimals"` 15 | Timestamp int `json:"timestamp"` 16 | ValueUnscaled types.UnscaledNumber `json:"valueUnscaled"` 17 | } 18 | 19 | var FloatZero = new(big.Float) 20 | 21 | func (obj *Amount) UsdValue(price string, fusdcAddr types.Address) (string, error) { 22 | value := obj.ValueUnscaled 23 | dividedAmt := value.Scale(obj.Decimals) //value / (10 ** decimals) 24 | switch obj.Token { 25 | case fusdcAddr: 26 | // 4 decimals 27 | return fmt.Sprintf("%0.8f", dividedAmt), nil 28 | default: 29 | //value / (10 ** decimals) * price 30 | x := new(big.Float).Set(dividedAmt) 31 | if price == "" { 32 | return "0", nil // Empty price. 33 | } 34 | priceFloat, ok := new(big.Float).SetString(price) 35 | if !ok { 36 | return "", fmt.Errorf("failed to set string: %#v", price) 37 | } 38 | if priceFloat.Cmp(FloatZero) == 0 { // Price is also empty (0). 39 | return "0", nil 40 | } 41 | x.Mul(dividedAmt, priceFloat) 42 | return fmt.Sprintf("%0.8f", x), nil 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/liquidity-campaign.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/fluidity-money/long.so/lib/types" 7 | ) 8 | 9 | // Liquidity campaigns available in this pool that's distributed on-chain. 10 | type LiquidityCampaign struct { 11 | Pool types.Address `json:"pool"` 12 | Token types.Address `json:"token"` 13 | TickLower int32 `json:"tick_lower"` 14 | TickUpper int32 `json:"tick_upper"` 15 | Owner types.Address `json:"owner"` 16 | Starting time.Time `json:"starting"` 17 | Ending time.Time `json:"ending"` 18 | } 19 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/liquidity.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // SeawaterLiquidity available in a pool summed and grouped by ticks. 4 | type SeawaterLiquidity struct { 5 | ID string `json:"id"` 6 | TickLower string `json:"tickLower"` 7 | TickUpper string `json:"tickUpper"` 8 | Price string `json:"price"` 9 | Liquidity string `json:"liquidity"` 10 | } 11 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/pagination.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/fluidity-money/long.so/lib/types" 5 | "github.com/fluidity-money/long.so/lib/types/seawater" 6 | ) 7 | 8 | // Pagination-friendly way of viewing the current state of the positions available in a pool. 9 | type SeawaterPositions struct { 10 | // From is used alongside To to find the cursor position in the 11 | // database scan. Pool is also used to filter for this. 12 | // We use the position id for everything. 13 | From int `json:"from"` 14 | To *int `json:"to"` 15 | 16 | // Pool is set, then we assume the filtering needs to happen for a pool. 17 | Pool *types.Address `json:"pool"` 18 | // Wallet is set, then we assume it needs to happen for a specific wallet. 19 | Wallet *types.Address 20 | 21 | Positions []seawater.Position `json:"positions"` 22 | } 23 | 24 | type ( 25 | SeawaterPositionsGlobal SeawaterPositions 26 | SeawaterPositionsUser SeawaterPositions 27 | ) 28 | 29 | // Pagination-friendly way to quickly receive swaps made somewhere. Knows internally where it 30 | // came from, where it's at with pagination, and lets you continue to paginate through it 31 | // optionally. 32 | type SeawaterSwaps struct { 33 | From int `json:"from"` 34 | To int `json:"to"` 35 | 36 | // Pool, if set, enables filtering based on the pool that's used here. 37 | Pool *types.Address `json:"pool"` 38 | // Wallet is set, then we assume it needs to happen for a specific wallet. 39 | Wallet *types.Address 40 | 41 | Swaps []SeawaterSwap `json:"swaps"` 42 | } 43 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/pool-config.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/fluidity-money/long.so/lib/config" 5 | "github.com/fluidity-money/long.so/lib/types" 6 | ) 7 | 8 | type SeawaterConfig struct { 9 | Addr types.Address 10 | config.Pool 11 | } 12 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/price.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/fluidity-money/long.so/lib/math" 5 | "github.com/fluidity-money/long.so/lib/types" 6 | ) 7 | 8 | type PriceResult struct { 9 | FinalTick types.Number `json:"final_tick"` 10 | } 11 | 12 | // Price to obtain the price from the final tick as a formatted float string 13 | func (p PriceResult) Price(fusdcDecimals, poolDecimals int) string { 14 | sqrtPrice := math.GetSqrtRatioAtTick(p.FinalTick.Big()) 15 | price := math.GetPriceAtSqrtRatio(sqrtPrice) 16 | decimals := math.ExponentiateDecimals(int64(poolDecimals - fusdcDecimals)) 17 | price.Mul(price, decimals) 18 | return price.FloatString(5) 19 | } 20 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/price_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/fluidity-money/long.so/lib/types" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPriceResultPrice(t *testing.T) { 11 | var ( 12 | p = PriceResult{ 13 | FinalTick: types.NumberFromInt64(-198101), 14 | } 15 | fusdcDecimals = 6 16 | poolDecimals = 18 17 | expected = "2494.66955" 18 | ) 19 | 20 | price := p.Price(fusdcDecimals, poolDecimals) 21 | assert.Equal(t, expected, price) 22 | 23 | p = PriceResult{ 24 | FinalTick: types.NumberFromInt64(123), 25 | } 26 | fusdcDecimals = 6 27 | poolDecimals = 6 28 | expected = "1.012375" 29 | } 30 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/seawater.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/fluidity-money/long.so/lib/types/seawater" 4 | 5 | type ( 6 | SeawaterPool = seawater.Pool 7 | SeawaterPosition = seawater.Position 8 | ) 9 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/swaps.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/fluidity-money/long.so/lib/types" 7 | ) 8 | 9 | type SeawaterSwap struct { 10 | CreatedBy time.Time `json:"createdBy"` 11 | Sender types.Address `json:"sender"` 12 | TokenIn types.Address `json:"tokenIn"` 13 | TokenInDecimals int `json:"tokenInDecimals"` 14 | TokenOut types.Address `json:"tokenOut"` 15 | TokenOutDecimals int `json:"tokenOutDecimals"` 16 | AmountIn types.UnscaledNumber `json:"amountIn"` 17 | AmountOut types.UnscaledNumber `json:"amountOut"` 18 | TransactionHash types.Hash `json:"transactionHash"` 19 | } 20 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/token.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/fluidity-money/long.so/lib/types/erc20" 4 | 5 | type Token struct{ erc20.Erc20 } 6 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/model/wallet.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/fluidity-money/long.so/lib/types" 4 | 5 | type Wallet struct { 6 | Address types.Address `json:"address"` 7 | } 8 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/graph/resolver.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | 6 | "github.com/ethereum/go-ethereum/ethclient" 7 | 8 | "github.com/fluidity-money/long.so/lib/config" 9 | "github.com/fluidity-money/long.so/lib/features" 10 | "github.com/fluidity-money/long.so/lib/types" 11 | ) 12 | 13 | type Resolver struct { 14 | DB *gorm.DB // db used to look up any fields that are missing from a request. 15 | F features.F // features to have enabled when requested 16 | Geth *ethclient.Client // needed to do lookups with geth 17 | C config.C // config for connecting to the right endpoints 18 | PoolsConfig map[types.Address]config.Pool // config for pools deployed only the backend knows. 19 | } 20 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/lib/erc20/erc20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_spender", 7 | "type": "address" 8 | } 9 | ], 10 | "name": "balanceOf", 11 | "outputs": [ 12 | { 13 | "internalType": "uint256", 14 | "name": "", 15 | "type": "uint256" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | }, 21 | { 22 | "inputs": [], 23 | "name": "decimals", 24 | "outputs": [ 25 | { 26 | "internalType": "uint8", 27 | "name": "", 28 | "type": "uint8" 29 | } 30 | ], 31 | "stateMutability": "view", 32 | "type": "function" 33 | }, 34 | { 35 | "inputs": [], 36 | "name": "name", 37 | "outputs": [ 38 | { 39 | "internalType": "string", 40 | "name": "", 41 | "type": "string" 42 | } 43 | ], 44 | "stateMutability": "view", 45 | "type": "function" 46 | }, 47 | { 48 | "inputs": [], 49 | "name": "symbol", 50 | "outputs": [ 51 | { 52 | "internalType": "string", 53 | "name": "", 54 | "type": "string" 55 | } 56 | ], 57 | "stateMutability": "view", 58 | "type": "function" 59 | }, 60 | { 61 | "inputs": [], 62 | "name": "totalSupply", 63 | "outputs": [ 64 | { 65 | "internalType": "uint256", 66 | "name": "", 67 | "type": "uint256" 68 | } 69 | ], 70 | "stateMutability": "view", 71 | "type": "function" 72 | } 73 | ] 74 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/lib/erc20/multicall.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "components": [ 6 | { 7 | "internalType": "address", 8 | "name": "target", 9 | "type": "address" 10 | }, 11 | { 12 | "internalType": "bytes", 13 | "name": "callData", 14 | "type": "bytes" 15 | } 16 | ], 17 | "internalType": "struct Multicall2.Call[]", 18 | "name": "calls", 19 | "type": "tuple[]" 20 | } 21 | ], 22 | "name": "aggregate", 23 | "outputs": [ 24 | { 25 | "internalType": "uint256", 26 | "name": "blockNumber", 27 | "type": "uint256" 28 | }, 29 | { 30 | "internalType": "bytes[]", 31 | "name": "returnData", 32 | "type": "bytes[]" 33 | } 34 | ], 35 | "stateMutability": "nonpayable", 36 | "type": "function" 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/pools.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | 6 | "github.com/fluidity-money/long.so/lib/config" 7 | "github.com/fluidity-money/long.so/lib/types" 8 | 9 | "github.com/pelletier/go-toml/v2" 10 | ) 11 | 12 | //go:embed pools.toml 13 | var poolsConfigBytes []byte 14 | 15 | // PoolsConfig loaded from pools.toml in the toplevel 16 | var PoolsConfig map[types.Address]config.Pool 17 | 18 | func init() { 19 | // Needed since the package doesn't do decoding for this properly. 20 | var c map[string]config.Pool 21 | if err := toml.Unmarshal(poolsConfigBytes, &c); err != nil { 22 | panic(err) 23 | } 24 | PoolsConfig = make(map[types.Address]config.Pool, len(c)) 25 | for k, v := range c { 26 | PoolsConfig[types.AddressFromString(k)] = v 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cmd/graphql.ethereum/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | import _ "github.com/99designs/gqlgen" 7 | -------------------------------------------------------------------------------- /cmd/ingestor.logs.ethereum/.dockerignore: -------------------------------------------------------------------------------- 1 | ingestor.logs.ethereum 2 | -------------------------------------------------------------------------------- /cmd/ingestor.logs.ethereum/.gitignore: -------------------------------------------------------------------------------- 1 | ingestor.logs.ethereum 2 | -------------------------------------------------------------------------------- /cmd/ingestor.logs.ethereum/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../golang.mk 3 | -------------------------------------------------------------------------------- /cmd/ingestor.logs.ethereum/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Ethereum Ingestor 3 | 4 | Ethereum ingestor simply reads data from the chain and stores it in the database for later 5 | retrieval. It subscribes to block headers, and it allows dependent services to determine 6 | if they need a log from the block header using the block header check function. 7 | 8 | ## Notes on finality 9 | 10 | Does not make any assumptions about finality. 11 | -------------------------------------------------------------------------------- /cmd/ingestor.logs.ethereum/polling-db.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type BlockCheckpoint struct { 10 | ID int 11 | LastUpdated time.Time 12 | BlockNumber uint64 13 | } 14 | 15 | func getLastBlockCheckpointed(db *gorm.DB) (uint64, error) { 16 | var c BlockCheckpoint 17 | err := db.Table("ingestor_checkpointing_1").Find(&c).Error 18 | if err != nil { 19 | return 0, err 20 | } 21 | return c.BlockNumber, nil 22 | } 23 | 24 | func updateCheckpoint(db *gorm.DB, blockNo uint64) error { 25 | err := db.Table("ingestor_checkpointing_1"). 26 | Save(&BlockCheckpoint{1, time.Now(), blockNo}). 27 | Error 28 | return err 29 | } 30 | -------------------------------------------------------------------------------- /cmd/ingestor.logs.ethereum/reflect.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "reflect" 4 | 5 | func setEventFields(a any, blockHash, transactionHash string, blockNo uint64, emitterAddr string) { 6 | v := reflect.Indirect(reflect.ValueOf(a)).Elem().Elem() 7 | v.FieldByName("BlockHash").SetString(blockHash) 8 | v.FieldByName("TransactionHash").SetString(transactionHash) 9 | v.FieldByName("BlockNumber").SetUint(blockNo) 10 | v.FieldByName("EmitterAddr").SetString(emitterAddr) 11 | } 12 | -------------------------------------------------------------------------------- /cmd/snapshot.ethereum/.gitignore: -------------------------------------------------------------------------------- 1 | snapshot.ethereum 2 | -------------------------------------------------------------------------------- /cmd/snapshot.ethereum/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../golang.mk 3 | -------------------------------------------------------------------------------- /cmd/snapshot.ethereum/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Snapshot Ethereum 3 | 4 | Snapshots every position tracked as a database event every 5 minutes (and up to 15 random 5 | seconds) by retrieving the position's tick range, and the position's delta. Writes each 6 | response to the database. 7 | 8 | Also snapshots liquidity groups to the new table. 9 | 10 | ## Note 11 | 12 | It's wise to protect this from running without any protection in the form of a lock. At a 13 | infra level we orchestrate this with a lockfile to prevent it from doubling up with our 14 | scheduling. 15 | -------------------------------------------------------------------------------- /cmd/snapshot.ethereum/database.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log/slog" 6 | "strconv" 7 | "strings" 8 | 9 | "gorm.io/gorm" 10 | ) 11 | 12 | // storePositions in the database, by formatting a string to use to make 13 | // the insertion. Thankfully we're protected by the datatype for this. 14 | func storePositions(db *gorm.DB, pools []string, ids []int, amount0s, amount1s []string) error { 15 | // Gorm lacks the support for inserting arrays (we think) so this 16 | // is something we need to do. Ugly I know. Thankfully this isn't used super often. 17 | idsS := make([]string, len(ids)) 18 | for i, id := range ids { 19 | idsS[i] = strconv.Itoa(id) 20 | } 21 | var bu strings.Builder 22 | for i, addr := range pools { 23 | fmt.Fprintf(&bu, `'%s'`, addr) 24 | if i != len(pools)-1 { 25 | bu.WriteRune(',') 26 | } 27 | } 28 | s := fmt.Sprintf( 29 | "SELECT snapshot_create_positions_1(ARRAY[%s], ARRAY[%s], ARRAY[%s], ARRAY[%s])", 30 | bu.String(), // pools 31 | strings.Join(idsS, ","), // ids 32 | strings.Join(amount0s, ","), // amount0s 33 | strings.Join(amount1s, ","), // amount1s 34 | ) 35 | slog.Debug("about to execute sql", "sql", s) 36 | err := db.Exec(s).Error 37 | return err 38 | } 39 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | ignore: 3 | - "cmd/faucet.superposition/graph/generated.go" 4 | - "cmd/faucet.superposition/graph/mocked.go" 5 | - "cmd/faucet.superposition/graph/model/models_gen.go" 6 | - "cmd/faucet.superposition/graph/resolver.go" 7 | - "cmd/faucet.superposition/graph/schema.resolvers.go" 8 | - "cmd/faucet.superposition/tools.go" 9 | - "cmd/graphql.ethereum/graph/generated.go" 10 | - "cmd/graphql.ethereum/graph/mocked.go" 11 | - "cmd/graphql.ethereum/graph/model/models_gen.go" 12 | - "cmd/graphql.ethereum/graph/resolver.go" 13 | - "cmd/graphql.ethereum/graph/schema.resolvers.go" 14 | - "cmd/graphql.ethereum/tools.go" 15 | 16 | coverage: 17 | status: 18 | project: 19 | default: 20 | target: auto 21 | threshold: 1% 22 | base: auto 23 | branches: 24 | - development 25 | if_ci_failed: error 26 | -------------------------------------------------------------------------------- /config/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Configs 3 | 4 | Configuration files that're distributed with the codebase. Should configure how the 5 | frontend displays data using the graph. 6 | -------------------------------------------------------------------------------- /config/pools.toml: -------------------------------------------------------------------------------- 1 | 2 | [0xA8EA92c819463EFbEdDFB670FEfC881A480f0115] 3 | displayed = true 4 | classification = "STABLECOIN" 5 | 6 | [0xde104342B32BCa03ec995f999181f7Cf1fFc04d7] 7 | displayed = true 8 | classification = "VOLATILE" 9 | 10 | [0x6437fdc89cED41941b97A9f1f8992D88718C81c5] 11 | displayed = true 12 | classification = "STABLECOIN" 13 | 14 | [0x09f7156aae9c903f90b1cb1e312582c4f208a759] 15 | displayed = true 16 | classification = "VOLATILE" 17 | 18 | [0x36c116a8851869cf8a99b3bda0fad42453d32b99] 19 | displayed = true 20 | classification = "VOLATILE" 21 | -------------------------------------------------------------------------------- /db/.gitignore: -------------------------------------------------------------------------------- 1 | db/ 2 | -------------------------------------------------------------------------------- /db/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM timescale/timescaledb:2.15.0-pg15 3 | 4 | WORKDIR /usr/local/src/superposition/database 5 | 6 | RUN apk add make curl 7 | 8 | RUN apk add postgresql-pg_cron \ 9 | && ls /usr/lib | grep postgres \ 10 | && cp /usr/lib/postgresql15/pg_cron.so /usr/local/lib/postgresql/ \ 11 | && ls /usr/share/postgresql/extension \ 12 | && cp /usr/share/postgresql/extension/* /usr/local/share/postgresql/extension 13 | 14 | RUN curl -fsSLo \ 15 | /usr/local/bin/dbmate \ 16 | https://github.com/amacneil/dbmate/releases/latest/download/dbmate-linux-amd64 17 | 18 | RUN chmod +x /usr/local/bin/dbmate 19 | 20 | COPY migrations/ . 21 | 22 | COPY init-database.sh /docker-entrypoint-initdb.d/init-sqls.sh 23 | -------------------------------------------------------------------------------- /db/Dockerfile.migrator: -------------------------------------------------------------------------------- 1 | 2 | FROM alpine:3.19 3 | 4 | RUN apk add make curl 5 | 6 | RUN curl -fsSLo \ 7 | /usr/local/bin/dbmate \ 8 | https://github.com/amacneil/dbmate/releases/latest/download/dbmate-linux-amd64 9 | 10 | RUN chmod +x /usr/local/bin/dbmate 11 | 12 | COPY migrations/ . 13 | 14 | ENV SPN_TIMESCALE_URL ${SPN_TIMESCALE_URL} 15 | 16 | ENTRYPOINT dbmate -u "$SPN_TIMESCALE_URL" -d migrations up 17 | -------------------------------------------------------------------------------- /db/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Longtail AMM indexing service database 3 | 4 | For quick indexing, some Ethereum-based primitives are available for simple access. These 5 | are not stored in a jsonb blob. They are based on Arbitrum, and should remain static. 6 | -------------------------------------------------------------------------------- /db/create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -u 2 | 3 | timestamp="$(date +%s)" 4 | 5 | name="$1" 6 | 7 | name="migrations/${timestamp}-${name}.sql" 8 | 9 | cat >"$name" < spl.lower 24 | ), 25 | cumulative_amounts AS ( 26 | SELECT 27 | pool, 28 | tick, 29 | next_tick, 30 | SUM(amount0) AS cumulative_amount0, 31 | SUM(amount1) AS cumulative_amount1 32 | FROM 33 | position_ticks 34 | GROUP BY 35 | pool, tick, next_tick 36 | ) 37 | SELECT 38 | pool, 39 | np.decimals, 40 | tick, 41 | next_tick, 42 | cumulative_amount0, 43 | cumulative_amount1 44 | FROM 45 | cumulative_amounts 46 | LEFT JOIN events_seawater_newPool np ON np.token = pool 47 | WHERE 48 | cumulative_amount0 > 0 OR cumulative_amount1 > 0 49 | ORDER BY 50 | tick; 51 | 52 | -- migrate:down 53 | -------------------------------------------------------------------------------- /db/migrations/1718793094-snapshot_latest_decimals.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE VIEW snapshot_positions_latest_decimals_1 AS 4 | SELECT 5 | snapshot_positions_latest_1.id AS id, 6 | updated_by, 7 | pos_id, 8 | owner, 9 | pool, 10 | lower, 11 | upper, 12 | amount0, 13 | amount1, 14 | pool.decimals AS decimals 15 | FROM 16 | snapshot_positions_latest_1 17 | LEFT JOIN events_seawater_newPool pool ON token = pool; 18 | 19 | CREATE VIEW snapshot_positions_latest_decimals_grouped_1 AS 20 | SELECT 21 | pool, 22 | decimals, 23 | SUM(amount0) AS cumulative_amount0, 24 | SUM(amount1) AS cumulative_amount1 25 | FROM 26 | snapshot_positions_latest_decimals_1 27 | GROUP BY 28 | pool, 29 | decimals; 30 | 31 | -- migrate:down 32 | -------------------------------------------------------------------------------- /db/migrations/1718795061-snapshot_latest_decimals_per_wallet.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE snapshot_positions_latest_decimals_grouped_user_1_return ( 4 | pool ADDRESS NOT NULL, 5 | decimals HUGEINT NOT NULL, 6 | cumulative_amount0 HUGEINT NOT NULL, 7 | cumulative_amount1 HUGEINT NOT NULL 8 | ); 9 | 10 | CREATE FUNCTION snapshot_positions_latest_decimals_grouped_user_1(wallet ADDRESS) 11 | RETURNS SETOF snapshot_positions_latest_decimals_grouped_user_1_return 12 | LANGUAGE SQL 13 | STABLE 14 | AS 15 | $$ 16 | SELECT 17 | pool, 18 | decimals, 19 | SUM(amount0) AS cumulative_amount0, 20 | SUM(amount1) AS cumulative_amount1 21 | FROM 22 | snapshot_positions_latest_decimals_1 23 | WHERE owner = wallet 24 | GROUP BY 25 | pool, 26 | decimals; 27 | $$; 28 | 29 | -- migrate:down 30 | -------------------------------------------------------------------------------- /db/migrations/1719380221-faucet_buffer_instead.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | ALTER TABLE faucet_requests 4 | ADD COLUMN was_sent BOOLEAN NOT NULL DEFAULT FALSE, 5 | ADD COLUMN is_fly_staker BOOLEAN NOT NULL DEFAULT FALSE; 6 | 7 | -- migrate:down 8 | -------------------------------------------------------------------------------- /db/migrations/1720264790-latest_ticks_1.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE VIEW seawater_latest_ticks_1 AS 4 | SELECT final_tick, created_by, pool 5 | FROM ( 6 | SELECT final_tick, created_by, pool 7 | FROM ( 8 | SELECT final_tick, created_by, pool, 9 | ROW_NUMBER() OVER (PARTITION BY pool ORDER BY created_by DESC) AS rn 10 | FROM events_seawater_swap1 11 | ) AS subquery1 12 | WHERE rn = 1 13 | UNION ALL 14 | SELECT final_tick0 AS final_tick, created_by, from_ AS pool 15 | FROM ( 16 | SELECT final_tick0, created_by, from_, 17 | ROW_NUMBER() OVER (PARTITION BY from_ ORDER BY created_by DESC) AS rn 18 | FROM events_seawater_swap2 19 | ) AS subquery2 20 | WHERE rn = 1 21 | UNION ALL 22 | SELECT final_tick1 AS final_tick, created_by, to_ AS pool 23 | FROM ( 24 | SELECT final_tick1, created_by, to_, 25 | ROW_NUMBER() OVER (PARTITION BY to_ ORDER BY created_by DESC) AS rn 26 | FROM events_seawater_swap2 27 | ) AS subquery3 28 | WHERE rn = 1 29 | ) AS swaps 30 | ORDER BY created_by DESC; 31 | 32 | -- migrate:down 33 | -------------------------------------------------------------------------------- /db/migrations/1720277523-erc20_cache.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE erc20_cache_1 ( 4 | id SERIAL PRIMARY KEY, 5 | address ADDRESS NOT NULL, 6 | name VARCHAR NOT NULL, 7 | symbol VARCHAR NOT NULL, 8 | total_supply HUGEINT NOT NULL, 9 | decimals INTEGER NOT NULL 10 | ); 11 | 12 | CREATE UNIQUE INDEX ON erc20_cache_1 (address); 13 | 14 | CREATE FUNCTION erc20_insert_1( 15 | a ADDRESS, 16 | n VARCHAR, 17 | s VARCHAR, 18 | t HUGEINT, 19 | d INTEGER 20 | ) 21 | RETURNS void LANGUAGE plpgsql 22 | AS $$ 23 | BEGIN 24 | INSERT INTO erc20_cache_1 (address, name, symbol, total_supply, decimals) 25 | VALUES (a, n, s, t, d) 26 | ON CONFLICT (address) DO NOTHING; 27 | END $$; 28 | 29 | -- migrate:down 30 | -------------------------------------------------------------------------------- /db/migrations/1720397832-seawater_liquidity_groups_2.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | -- Copy the state from the existing liquidity groups view to a new table on demand. 4 | 5 | CREATE TABLE seawater_liquidity_groups_2 ( 6 | pool ADDRESS NOT NULL, 7 | decimals HUGEINT NOT NULL, 8 | tick INTEGER NOT NULL, 9 | next_tick INTEGER NOT NULL, 10 | cumulative_amount0 NUMERIC NOT NULL, 11 | cumulative_amount1 NUMERIC NOT NULL 12 | ); 13 | 14 | CREATE FUNCTION snapshot_liquidity_groups_1() 15 | RETURNS VOID LANGUAGE PLPGSQL 16 | AS $$ 17 | BEGIN 18 | DELETE FROM seawater_liquidity_groups_2; 19 | INSERT INTO seawater_liquidity_groups_2 SELECT * FROM seawater_liquidity_groups_1; 20 | END $$; 21 | 22 | -- migrate:down 23 | -------------------------------------------------------------------------------- /db/migrations/1720503733-seawater_final_ticks_daily_2.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE seawater_final_ticks_daily_2 ( 4 | final_tick BIGINT NOT NULL, 5 | pool ADDRESS NOT NULL, 6 | day TIMESTAMP WITHOUT TIME ZONE 7 | ); 8 | 9 | CREATE FUNCTION snapshot_final_ticks_daily_1() 10 | RETURNS VOID LANGUAGE PLPGSQL 11 | AS $$ 12 | BEGIN 13 | DELETE FROM seawater_final_ticks_daily_2; 14 | INSERT INTO seawater_final_ticks_daily_2 SELECT * FROM seawater_final_ticks_daily_1; 15 | END $$; 16 | 17 | -- migrate:down 18 | -------------------------------------------------------------------------------- /db/migrations/1720504185-seawater_final_ticks_monthly_2.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE seawater_final_ticks_monthly_2 ( 4 | final_tick BIGINT NOT NULL, 5 | pool ADDRESS NOT NULL, 6 | month TIMESTAMP WITHOUT TIME ZONE 7 | ); 8 | 9 | CREATE FUNCTION snapshot_final_ticks_monthly_2() 10 | RETURNS VOID LANGUAGE PLPGSQL 11 | AS $$ 12 | BEGIN 13 | DELETE FROM seawater_final_ticks_monthly_2; 14 | INSERT INTO seawater_final_ticks_daily_2 SELECT * FROM seawater_final_ticks_monthly_1; 15 | END $$; 16 | 17 | -- migrate:down 18 | -------------------------------------------------------------------------------- /db/migrations/1720505013-seawater_latest_ticks_2.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE seawater_latest_ticks_2 ( 4 | final_tick BIGINT NOT NULL, 5 | created_by TIMESTAMP WITHOUT TIME ZONE NOT NULL, 6 | pool ADDRESS NOT NULL 7 | ); 8 | 9 | CREATE FUNCTION snapshot_latest_ticks_1() 10 | RETURNS VOID LANGUAGE PLPGSQL 11 | AS $$ 12 | BEGIN 13 | DELETE FROM seawater_latest_ticks_2; 14 | INSERT INTO seawater_latest_ticks_2 SELECT * FROM seawater_latest_ticks_1; 15 | END $$; 16 | 17 | -- migrate:down 18 | -------------------------------------------------------------------------------- /db/migrations/1720506347-seawater_positions_2.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE seawater_active_positions_2 ( 4 | created_by TIMESTAMP WITHOUT TIME ZONE NOT NULL, 5 | block_hash HASH NOT NULL, 6 | transaction_hash HASH NOT NULL, 7 | created_block_number INTEGER NOT NULL, 8 | pos_id HUGEINT NOT NULL, 9 | owner ADDRESS NOT NULL, 10 | pool ADDRESS NOT NULL, 11 | lower BIGINT NOT NULL, 12 | upper BIGINT NOT NULL 13 | ); 14 | 15 | CREATE FUNCTION snapshot_seawater_active_positions() 16 | RETURNS VOID LANGUAGE PLPGSQL 17 | AS $$ 18 | BEGIN 19 | DELETE FROM seawater_active_positions_2; 20 | INSERT INTO seawater_active_positions_2 SELECT * FROM seawater_active_positions_1; 21 | END $$; 22 | 23 | SELECT cron.schedule('update seawater positions', '*/1 * * * *', 'SELECT snapshot_seawater_active_positions();'); 24 | 25 | -- migrate:down 26 | -------------------------------------------------------------------------------- /db/migrations/1723013702-events_thirdweb.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE events_thirdweb_accountcreated ( 4 | id SERIAL PRIMARY KEY, 5 | created_by TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, 6 | block_hash HASH NOT NULL, 7 | transaction_hash HASH NOT NULL, 8 | block_number INTEGER NOT NULL, 9 | emitter_addr ADDRESS NOT NULL, 10 | 11 | account ADDRESS NOT NULL, 12 | account_admin ADDRESS NOT NULL 13 | ); 14 | 15 | -- migrate:down 16 | -------------------------------------------------------------------------------- /db/migrations/1723190765-seawater_cron_some_snapshot_functions.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | SELECT cron.schedule('update daily ticks', '0 * * * *', 'snapshot_final_ticks_daily_1()'); 4 | 5 | SELECT cron.schedule('update monthly ticks', '0 * * * *', 'snapshot_final_ticks_monthly_2()'); 6 | 7 | SELECT cron.schedule('update liquidity grouping', '*/30 * * * *', 'snapshot_liquidity_groups_1()'); 8 | 9 | SELECT cron.schedule('update latest ticks', '*/30 * * * *', 'snapshot_latest_ticks_1()'); 10 | 11 | -- migrate:down 12 | -------------------------------------------------------------------------------- /db/migrations/1723693657-events_leo.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE events_leo_campaignbalanceupdated ( 4 | id SERIAL PRIMARY KEY, 5 | created_by TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, 6 | block_hash HASH NOT NULL, 7 | transaction_hash HASH NOT NULL, 8 | block_number INTEGER NOT NULL, 9 | emitter_addr ADDRESS NOT NULL, 10 | 11 | identifier VARCHAR NOT NULL, 12 | new_maximum HUGEINT NOT NULL 13 | ); 14 | 15 | CREATE TABLE events_leo_campaigncreated ( 16 | id SERIAL PRIMARY KEY, 17 | created_by TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, 18 | block_hash HASH NOT NULL, 19 | transaction_hash HASH NOT NULL, 20 | block_number INTEGER NOT NULL, 21 | emitter_addr ADDRESS NOT NULL, 22 | 23 | identifier VARCHAR NOT NULL, 24 | pool ADDRESS NOT NULL, 25 | token ADDRESS NOT NULL, 26 | tick_lower INTEGER NOT NULL, 27 | tick_upper INTEGER NOT NULL, 28 | owner ADDRESS NOT NULL, 29 | starting TIMESTAMP WITHOUT TIME ZONE, 30 | ending TIMESTAMP WITHOUT TIME ZONE 31 | ); 32 | 33 | CREATE UNIQUE INDEX ON events_leo_campaigncreated (identifier, pool); 34 | 35 | -- migrate:down 36 | -------------------------------------------------------------------------------- /db/migrations/1723799478-events_leo_updated.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE events_leo_campaignupdated ( 4 | id SERIAL PRIMARY KEY, 5 | created_by TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, 6 | block_hash HASH NOT NULL, 7 | transaction_hash HASH NOT NULL, 8 | block_number INTEGER NOT NULL, 9 | emitter_addr ADDRESS NOT NULL, 10 | 11 | identifier VARCHAR NOT NULL, 12 | pool ADDRESS NOT NULL, 13 | per_second HUGEINT NOT NULL, 14 | tick_lower INTEGER NOT NULL, 15 | tick_upper INTEGER NOT NULL, 16 | starting TIMESTAMP WITHOUT TIME ZONE, 17 | ending TIMESTAMP WITHOUT TIME ZONE 18 | ); 19 | 20 | -- migrate:down 21 | -------------------------------------------------------------------------------- /db/migrations/1724121172-seawater_final_ticks_monthly_3.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE seawater_final_ticks_monthly_3 ( 4 | final_tick BIGINT NOT NULL, 5 | pool ADDRESS NOT NULL, 6 | month TIMESTAMP WITHOUT TIME ZONE 7 | ); 8 | 9 | CREATE FUNCTION snapshot_final_ticks_monthly_3() 10 | RETURNS VOID LANGUAGE PLPGSQL 11 | AS $$ 12 | BEGIN 13 | DELETE FROM seawater_final_ticks_monthly_3; 14 | INSERT INTO seawater_final_ticks_monthly_3 SELECT * FROM seawater_final_ticks_monthly_1; 15 | END $$; 16 | 17 | -- migrate:down 18 | -------------------------------------------------------------------------------- /db/migrations/1724121175-seawater_cron_snapshot_functions.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | SELECT cron.unschedule('update daily ticks'); 4 | SELECT cron.unschedule('update monthly ticks'); 5 | 6 | SELECT cron.schedule('update daily ticks', '0 * * * *', 'snapshot_final_ticks_daily_2()'); 7 | 8 | SELECT cron.schedule('update monthly ticks', '0 * * * *', 'snapshot_final_ticks_monthly_3()'); 9 | 10 | -- migrate:down 11 | -------------------------------------------------------------------------------- /db/migrations/1724128480-seawater_final_ticks_daily_3.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | CREATE TABLE seawater_final_ticks_daily_3 ( 4 | final_tick BIGINT NOT NULL, 5 | pool ADDRESS NOT NULL, 6 | day TIMESTAMP WITHOUT TIME ZONE 7 | ); 8 | 9 | CREATE FUNCTION snapshot_final_ticks_daily_3() 10 | RETURNS VOID LANGUAGE PLPGSQL 11 | AS $$ 12 | BEGIN 13 | DELETE FROM seawater_final_ticks_daily_3; 14 | INSERT INTO seawater_final_ticks_daily_3 SELECT * FROM seawater_final_ticks_daily_1; 15 | END $$; 16 | 17 | -- migrate:down 18 | -------------------------------------------------------------------------------- /db/migrations/1724128481-seawater_cron_fix_all_functions.sql: -------------------------------------------------------------------------------- 1 | -- migrate:up 2 | 3 | -- these functions were previously run without SELECT, causing them to fail unconditionally 4 | SELECT cron.unschedule('update daily ticks'); 5 | SELECT cron.unschedule('update monthly ticks'); 6 | SELECT cron.unschedule('update liquidity grouping'); 7 | SELECT cron.unschedule('update latest ticks'); 8 | 9 | SELECT cron.schedule('update daily ticks', '0 * * * *', 'SELECT snapshot_final_ticks_daily_3()'); 10 | 11 | SELECT cron.schedule('update monthly ticks', '0 * * * *', 'SELECT snapshot_final_ticks_monthly_3()'); 12 | 13 | SELECT cron.schedule('update liquidity grouping', '*/30 * * * *', 'SELECT snapshot_liquidity_groups_1()'); 14 | SELECT cron.schedule('update latest ticks', '*/30 * * * *', 'SELECT snapshot_latest_ticks_1()'); 15 | 16 | -- migrate:down 17 | -------------------------------------------------------------------------------- /db/update_and_migrate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Navigate to the specified directory 4 | cd /home/ec2-user/amm.superposition.so/db/migrations || exit 5 | 6 | # Perform a git pull 7 | git_output=$(git pull) 8 | 9 | # Check if there are changes 10 | if [[ $git_output != "Already up to date." ]]; then 11 | echo "Changes detected, running dbmate..." 12 | dbmate -u "$SPN_TIMESCALE" -d . up 13 | else 14 | echo "No changes detected." 15 | fi 16 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/021eb22c1e54e7d6b7976608d2a09da4-3028E.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/021eb22c1e54e7d6b7976608d2a09da4-3028E.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/045bf49f3bb3ecd3ddfc009a6af823ba-EF9F6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/045bf49f3bb3ecd3ddfc009a6af823ba-EF9F6.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/059d0fe5cf95abe78825804be97230dc-0FE6E.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/059d0fe5cf95abe78825804be97230dc-0FE6E.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1-B2132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1-B2132.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1f3c1-445DC.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1f440-6C64D.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1f451-B565E.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1f50d-195C0.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1f525-8FE4F.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1f642-83E8A.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1f680-A35CE.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/1f6a8-A8AB3.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/26a0-D845B.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/2764-A3D25.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/2a9faff195fe333526cfe6ae6fce1420-49B98.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/2a9faff195fe333526cfe6ae6fce1420-49B98.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/34d2d7de2893f18e31aa4da767ee758f-58D64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/34d2d7de2893f18e31aa4da767ee758f-58D64.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/4da13429705aea77fddae1baedc0844a-B2E15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/4da13429705aea77fddae1baedc0844a-B2E15.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/4de8e7cb6dfbbe8795697f1df8d66439-044AF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/4de8e7cb6dfbbe8795697f1df8d66439-044AF.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/4eaf99d01e4b5042454b4a6a8809687a-E5BB7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/4eaf99d01e4b5042454b4a6a8809687a-E5BB7.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/522f23dff5218659cb3b5477db3a097c-4BA21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/522f23dff5218659cb3b5477db3a097c-4BA21.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/546f74664414743e95af6c3045a13083-799B6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/546f74664414743e95af6c3045a13083-799B6.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/5b71d15a9bcde45fd5520e3fb580eb53-E1A50.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/67594ee4b4d1fc03bca468327a0d145b-BD76A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/67594ee4b4d1fc03bca468327a0d145b-BD76A.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/6eef2e8f35be0adcb2bd829d1a16f2a2-F341B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/6eef2e8f35be0adcb2bd829d1a16f2a2-F341B.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/851893157188599838-C23B5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/851893157188599838-C23B5.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/851893827089727568-5FD38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/851893827089727568-5FD38.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/851893827315826708-F59C0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/851893827315826708-F59C0.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/970d2e2f00cd7ef2134a1a3f21326349-404EA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/970d2e2f00cd7ef2134a1a3f21326349-404EA.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/9ccd56ad-107b-4841-9f4e-02f8195734c9-97D78: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/9ccd56ad-107b-4841-9f4e-02f8195734c9-97D78 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/a2bb0691f7aada3a9f92c6ea95ad2575-BFEEB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/a2bb0691f7aada3a9f92c6ea95ad2575-BFEEB.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/a7c8cf829ec618d22e5d0052ebaa015b-25CF4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/a7c8cf829ec618d22e5d0052ebaa015b-25CF4.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/a_1bf1676e45a74aaa9b4a2f0fd1d9a798-D2471.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/a_1bf1676e45a74aaa9b4a2f0fd1d9a798-D2471.gif -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/c894b6c4af55a46fb2d1aef0cd4cf6b0-27286.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/c894b6c4af55a46fb2d1aef0cd4cf6b0-27286.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/d059d6d6c98a3781344652bde0248969-CF180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/d059d6d6c98a3781344652bde0248969-CF180.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/e9d8cf0d70dd56df7330dd84eb234b69-0CF9D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/e9d8cf0d70dd56df7330dd84eb234b69-0CF9D.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-400-E988B.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-400-E988B.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-500-0777F.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-500-0777F.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-600-CB411.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-600-CB411.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-700-891AC.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-700-891AC.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-800-D36B0.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-italic-800-D36B0.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-400-1456D.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-400-1456D.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-500-89CE5.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-500-89CE5.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-600-C1EA8.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-600-C1EA8.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-700-1949A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-700-1949A.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-800-58487.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/ggsans-normal-800-58487.woff2 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/image-761DC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/image-761DC.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/image-928BE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/image-928BE.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/image-BDEF9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/image-BDEF9.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].html_Files/solarized-dark.min-BA98F.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#002b36;color:#839496}.hljs-comment,.hljs-quote{color:#586e75}.hljs-keyword,.hljs-selector-tag,.hljs-addition{color:#859900}.hljs-number,.hljs-string,.hljs-meta .hljs-meta-string,.hljs-literal,.hljs-doctag,.hljs-regexp{color:#2aa198}.hljs-title,.hljs-section,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#268bd2}.hljs-attribute,.hljs-attr,.hljs-variable,.hljs-template-variable,.hljs-class .hljs-title,.hljs-type{color:#b58900}.hljs-symbol,.hljs-bullet,.hljs-subst,.hljs-meta,.hljs-meta .hljs-keyword,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-link{color:#cb4b16}.hljs-built_in,.hljs-deletion{color:#dc322f}.hljs-formula{background:#073642}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files/9ccd56ad-107b-4841-9f4e-02f8195734c9-97D78: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files/9ccd56ad-107b-4841-9f4e-02f8195734c9-97D78 -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files/image-761DC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files/image-761DC.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files/image-928BE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files/image-928BE.png -------------------------------------------------------------------------------- /discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files/image-BDEF9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/discord-export/Code4rena - ARCHIVE-PUBLIC - superposition-aug23 [1269057494437728327].txt_Files/image-BDEF9.png -------------------------------------------------------------------------------- /features/features.json: -------------------------------------------------------------------------------- 1 | { 2 | "faucet stakers only": false, 3 | "faucet enabled": true, 4 | "graphql mock demo data": false, 5 | "graphql mock demo data should delay": false, 6 | "ui show boost incentives": false, 7 | "ui show campaign banner": false, 8 | "ui show claim all yield": true, 9 | "ui show claim yield": true, 10 | "ui show demo data": false, 11 | "ui show earned fees apr": false, 12 | "ui show feature flags panel": false, 13 | "ui show fee tier": false, 14 | "ui show incentives": false, 15 | "ui show liquidity incentives": false, 16 | "ui show live utility rewards": false, 17 | "ui show my transactions": false, 18 | "ui show optimising fee route": false, 19 | "ui show pool filters": false, 20 | "ui show pool reward range": false, 21 | "ui show pools tab": false, 22 | "ui show rewards claimed": false, 23 | "ui show stake apy": false, 24 | "ui show super incentives": false, 25 | "ui show superloop": false, 26 | "ui show swap breakdown": false, 27 | "ui show tokens given out": false, 28 | "ui show trade rewards": false, 29 | "ui show utility incentives": false, 30 | "ui show yield over time": false, 31 | "ui show liquidity visualiser": true 32 | } 33 | 34 | -------------------------------------------------------------------------------- /lib/config/defaults.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/fluidity-money/long.so/lib/types" 5 | "github.com/fluidity-money/long.so/lib/types/seawater" 6 | ) 7 | 8 | const ( 9 | // DefaultFusdcDecimals to use as the default for the base asset 10 | DefaultFusdcDecimals = 6 11 | 12 | // DefaultFusdcSymbol to send to users (from Superposition Testnet) 13 | DefaultFusdcSymbol = "fUSDC" 14 | 15 | // DefaultFusdcName to use (from Superposition Testnet) 16 | DefaultFusdcName = "Fluid USDC" 17 | ) 18 | 19 | // DefaultPoolConfig, for when we haven't identified the pool manually in 20 | // the past. 21 | var DefaultPoolConfiguration = Pool{ 22 | Displayed: true, 23 | Classification: seawater.ClassificationUnknown, 24 | } 25 | 26 | // DefaultFusdcTotalSupply from Superposition Testnet 27 | var DefaultFusdcTotalSupply = mustUnscaled("999999999999999999900750000") 28 | 29 | func mustUnscaled(s string) types.UnscaledNumber { 30 | x, err := types.UnscaledNumberFromBase10(s) 31 | if err != nil { 32 | panic(err) 33 | } 34 | return *x 35 | } 36 | -------------------------------------------------------------------------------- /lib/config/pools.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/fluidity-money/long.so/lib/types/seawater" 4 | 5 | // Pool config, probably derived from config/pools.toml. 6 | type Pool struct { 7 | Displayed bool `toml:"displayed" json:"displayed"` 8 | Classification seawater.Classification `toml:"classification" json:"classification"` 9 | } 10 | -------------------------------------------------------------------------------- /lib/events/erc20/abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "name": "from", 8 | "type": "address" 9 | }, 10 | { 11 | "indexed": true, 12 | "name": "to", 13 | "type": "address" 14 | }, 15 | { 16 | "indexed": false, 17 | "name": "value", 18 | "type": "uint256" 19 | } 20 | ], 21 | "name": "Transfer", 22 | "type": "event" 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /lib/events/erc20/erc20.go: -------------------------------------------------------------------------------- 1 | package erc20 2 | 3 | import ( 4 | "bytes" 5 | _ "embed" 6 | "fmt" 7 | "math/big" 8 | 9 | "github.com/fluidity-money/long.so/lib/types" 10 | 11 | ethAbi "github.com/ethereum/go-ethereum/accounts/abi" 12 | ethCommon "github.com/ethereum/go-ethereum/common" 13 | ) 14 | 15 | //go:embed abi.json 16 | var abiBytes []byte 17 | 18 | var abi, _ = ethAbi.JSON(bytes.NewReader(abiBytes)) 19 | 20 | // TopicTransfer emitted by Transfer(address,uint256) 21 | var TopicTransfer = abi.Events["Transfer"].ID 22 | 23 | func UnpackTransfer(topic1, topic2 ethCommon.Hash, d []byte) (*Transfer, error) { 24 | amount := new(big.Int) // Set to 0 as default, if the data is empty it will be 0 25 | if len(d) > 0 { 26 | i, err := abi.Unpack("Transfer", d) 27 | if err != nil { 28 | return nil, err 29 | } 30 | var ok bool 31 | amount, ok = i[0].(*big.Int) 32 | if !ok { 33 | return nil, fmt.Errorf("bad amount: %T", i[0]) 34 | } 35 | } 36 | return &Transfer{ 37 | Sender: hashToAddr(topic1), 38 | Recipient: hashToAddr(topic2), 39 | Value: types.UnscaledNumberFromBig(amount), 40 | }, nil 41 | } 42 | 43 | func hashToAddr(h ethCommon.Hash) types.Address { 44 | v := ethCommon.BytesToAddress(h.Bytes()) 45 | return types.Address(v.String()) 46 | } 47 | -------------------------------------------------------------------------------- /lib/events/erc20/types.go: -------------------------------------------------------------------------------- 1 | package erc20 2 | 3 | import ( 4 | "github.com/fluidity-money/long.so/lib/types" 5 | "github.com/fluidity-money/long.so/lib/events" 6 | ) 7 | 8 | type Transfer struct { 9 | events.Event 10 | 11 | Sender types.Address `json:"sender"` 12 | Recipient types.Address `json:"recipient"` 13 | Value types.UnscaledNumber `json:"value"` 14 | } 15 | -------------------------------------------------------------------------------- /lib/events/events.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/fluidity-money/long.so/lib/types" 7 | ) 8 | 9 | type Event struct { 10 | CreatedBy time.Time `json:"createdBy"` 11 | BlockHash types.Hash `json:"blockHash"` 12 | TransactionHash types.Hash `json:"transactionHash"` 13 | BlockNumber uint64 `json:"blockNumber"` 14 | EmitterAddr types.Address `json:"emitterAddr"` 15 | } 16 | -------------------------------------------------------------------------------- /lib/events/leo/types.go: -------------------------------------------------------------------------------- 1 | package leo 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/fluidity-money/long.so/lib/events" 7 | "github.com/fluidity-money/long.so/lib/types" 8 | ) 9 | 10 | type ( 11 | CampaignBalanceUpdated struct { 12 | events.Event 13 | 14 | Identifier types.Data `json:"identifier"` 15 | NewMaximum types.UnscaledNumber `json:"newMaximum"` 16 | } 17 | 18 | // CampaignCreated, unpacked in the local function with some of 19 | // the concatenated fields. 20 | CampaignCreated struct { 21 | events.Event 22 | 23 | Identifier types.Data `json:"identifier"` 24 | Pool types.Address `json:"pool"` 25 | Token types.Address `json:"token"` 26 | TickLower int32 `json:"tickLower"` 27 | TickUpper int32 `json:"tickUpper"` 28 | Owner types.Address `json:"owner"` 29 | Starting time.Time `json:"starting"` 30 | Ending time.Time `json:"ending"` 31 | } 32 | 33 | CampaignUpdated struct { 34 | events.Event 35 | 36 | Identifier types.Data `json:"identifier"` 37 | Pool types.Address `json:"pool"` 38 | PerSecond types.Number `json:"perSecond"` 39 | Token types.Address `json:"token"` 40 | TickLower int32 `json:"tickLower"` 41 | TickUpper int32 `json:"tickUpper"` 42 | Starting time.Time `json:"starting"` 43 | Ending time.Time `json:"ending"` 44 | } 45 | ) 46 | -------------------------------------------------------------------------------- /lib/events/thirdweb/abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "account", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "accountAdmin", 15 | "type": "address" 16 | } 17 | ], 18 | "name": "AccountCreated", 19 | "type": "event" 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /lib/events/thirdweb/thirdweb.go: -------------------------------------------------------------------------------- 1 | package thirdweb 2 | 3 | import ( 4 | "bytes" 5 | _ "embed" 6 | 7 | "github.com/fluidity-money/long.so/lib/types" 8 | 9 | ethCommon "github.com/ethereum/go-ethereum/common" 10 | ethAbi "github.com/ethereum/go-ethereum/accounts/abi" 11 | ) 12 | 13 | //go:embed abi.json 14 | var abiBytes []byte 15 | 16 | var abi, _ = ethAbi.JSON(bytes.NewReader(abiBytes)) 17 | 18 | // TopicAccountCreated emitted by Account 19 | var TopicAccountCreated = abi.Events["AccountCreated"].ID 20 | 21 | func UnpackAccountCreated(topic1, topic2 ethCommon.Hash, d []byte) (*AccountCreated, error) { 22 | var ( 23 | account = hashToAddr(topic1) 24 | accountAdmin = hashToAddr(topic2) 25 | ) 26 | return &AccountCreated{ 27 | Account: account, 28 | AccountAdmin: accountAdmin, 29 | }, nil 30 | } 31 | 32 | func hashToAddr(h ethCommon.Hash) types.Address { 33 | v := ethCommon.BytesToAddress(h.Bytes()) 34 | return types.AddressFromString(v.String()) 35 | } 36 | -------------------------------------------------------------------------------- /lib/events/thirdweb/types.go: -------------------------------------------------------------------------------- 1 | package thirdweb 2 | 3 | import ( 4 | "github.com/fluidity-money/long.so/lib/events" 5 | "github.com/fluidity-money/long.so/lib/types" 6 | ) 7 | 8 | type AccountCreated struct { 9 | events.Event 10 | 11 | Account types.Address `json:"account"` 12 | AccountAdmin types.Address `json:"accountAdmin"` 13 | } 14 | -------------------------------------------------------------------------------- /lib/features/features_test.go: -------------------------------------------------------------------------------- 1 | package features 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestBasicFeatureEnabled(t *testing.T) { 11 | f := F{false, map[string]bool{ 12 | "i'm alive!": true, 13 | }} 14 | assert.True(t, f.Is("i'm alive!")) 15 | } 16 | 17 | func TestBasicFeatureEverything(t *testing.T) { 18 | f := F{true, nil} 19 | assert.True(t, f.Is("i'm still standing!")) 20 | } 21 | 22 | func TestBasicFeatureNothing(t *testing.T) { 23 | f := F{false, nil} 24 | assert.False(t, f.Is("i'm still standing!")) 25 | } 26 | 27 | func TestPropagateErrorsUpOrNilEverything(t *testing.T) { 28 | f := F{true, nil} 29 | err := fmt.Errorf("error here") 30 | assert.Equal(t, err, f.On("i'm active!", func() error { 31 | return err 32 | })) 33 | } 34 | 35 | func TestPropagateErrorsNilEverything(t *testing.T) { 36 | f := F{true, nil} 37 | assert.Equal(t, nil, f.On("i'm active!", func() error { 38 | return nil 39 | })) 40 | } 41 | -------------------------------------------------------------------------------- /lib/features/list.go: -------------------------------------------------------------------------------- 1 | // list contains the list of features currently supported in the Go codebase. 2 | 3 | package features 4 | 5 | const ( 6 | // FeatureGraphqlMockGraph by sending mocked data instead of database data. 7 | FeatureGraphqlMockGraph = "graphql mock demo data" 8 | 9 | // FeatureGraphqlMockGraphDelay by delaying the display of the mocked data. 10 | FeatureGraphqlMockGraphDataDelay = "graphql mock demo data delay" 11 | 12 | // FeatureIngestorPollRpc using the ingestor. Useful in environments 13 | // where websocket access is inconsistent or unavailable. Does so with 14 | // a (by default) 15 second delay, with checkpointing done in the database. 15 | FeatureIngestorPollRpc = "ingestor poll rpc" 16 | 17 | // FeatureFaucetStakersOnly to gate access to the faucet to 18 | // through the moderators graph. 19 | FeatureFaucetStakersOnly = "faucet stakers only" 20 | 21 | // FeatureFaucetEnabled is allowed to be used. 22 | FeatureFaucetEnabled = "faucet enabled" 23 | ) 24 | -------------------------------------------------------------------------------- /lib/heartbeat/heartbeat.go: -------------------------------------------------------------------------------- 1 | // heartbeat: notify a system upstream that we're alive by making a HTTP 2 | // request. Optionally configured with environment variable 3 | // SPN_HEARTBEAT_URL. 4 | 5 | package heartbeat 6 | 7 | import ( 8 | "log/slog" 9 | "net/http" 10 | "os" 11 | ) 12 | 13 | // EnvHeartbeatUrl to optionally send a GET to on request. 14 | const EnvHeartbeatUrl = "SPN_HEARTBEAT_URL" 15 | 16 | var urls = make(chan string) 17 | 18 | func Pulse() { 19 | u := <-urls 20 | if u == "" { 21 | slog.Debug("skipping request to send message to heartbeat url") 22 | return 23 | } 24 | slog.Debug("sending a heartbeat message") 25 | resp, err := http.Get(u) 26 | if err != nil { 27 | slog.Error("error reporting to heartbeat", "err", err) 28 | return 29 | } 30 | resp.Body.Close() 31 | } 32 | 33 | func init() { 34 | s := os.Getenv(EnvHeartbeatUrl) 35 | if s == "" { 36 | slog.Info("heartbeat imported, but empty env", "env", EnvHeartbeatUrl) 37 | } 38 | go func() { 39 | for { 40 | urls <- s 41 | } 42 | }() 43 | } 44 | -------------------------------------------------------------------------------- /lib/math/decimals.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import "math/big" 4 | 5 | func ExponentiateDecimals(decimals int64) *big.Rat { 6 | d := new(big.Int).SetInt64(10) 7 | d.Exp(d, new(big.Int).SetInt64(int64(decimals)), nil) 8 | return new(big.Rat).SetInt(d) 9 | } 10 | -------------------------------------------------------------------------------- /lib/math/decimals_test.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestExponentiateDecimals(t *testing.T) { 11 | decimals := ExponentiateDecimals(6) 12 | expected := big.NewRat(1000000, 1) 13 | assert.Equal(t, expected, decimals) 14 | 15 | decimals = ExponentiateDecimals(18) 16 | expected = big.NewRat(1000000000000000000, 1) 17 | assert.Equal(t, expected, decimals) 18 | } 19 | -------------------------------------------------------------------------------- /lib/types/erc20/erc20.go: -------------------------------------------------------------------------------- 1 | package erc20 2 | 3 | import "github.com/fluidity-money/long.so/lib/types" 4 | 5 | type Erc20 struct { 6 | Address types.Address `json:"address"` 7 | Name string `json:"name"` 8 | Symbol string `json:"symbol"` 9 | TotalSupply types.UnscaledNumber `json:"total_supply"` 10 | Decimals int `json:"decimals"` 11 | } 12 | -------------------------------------------------------------------------------- /lib/types/seawater/classifications.go: -------------------------------------------------------------------------------- 1 | package seawater 2 | 3 | type Classification string 4 | 5 | const ( 6 | ClassificationStablecoin Classification = "STABLECOIN" 7 | ClassificationVolatile Classification = "VOLATILE" 8 | ClassificationUnknown Classification = "UNKNOWN" 9 | ) 10 | -------------------------------------------------------------------------------- /pkg/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # target = "wasm32-unknown-unknown" 3 | 4 | [target.wasm32-unknown-unknown] 5 | rustflags = [ 6 | "-C", "link-arg=-zstack-size=8192", # shrink the heap 7 | ] 8 | -------------------------------------------------------------------------------- /pkg/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | out/ 17 | cache/ 18 | 19 | seawater-admin.wasm 20 | seawater-positions.wasm 21 | seawater-quotes.wasm 22 | seawater-swap-permit2.wasm 23 | seawater-swaps.wasm 24 | seawater-update-positions.wasm 25 | seawater-migrations.wasm 26 | 27 | node_modules/ 28 | 29 | docker 30 | .DS_Store 31 | docs-out/ 32 | 33 | tsconfig.tsbuildinfo 34 | yarn.lock 35 | -------------------------------------------------------------------------------- /pkg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | resolver = "2" 4 | 5 | members = [ 6 | "seawater", 7 | "leo", 8 | ] 9 | 10 | [profile.release] 11 | codegen-units = 1 12 | panic = "abort" 13 | opt-level = "z" 14 | strip = true 15 | lto = true 16 | debug = false 17 | rpath = false 18 | debug-assertions = false 19 | incremental = false 20 | 21 | [workspace.dependencies] 22 | stylus-sdk = "0.5.2" 23 | thiserror = "1.0.48" 24 | tiny-keccak = "2.0.2" 25 | ruint = { version = "1.11.0", features = ["num-traits"] } 26 | num-traits = "0.2.19" 27 | alloy-sol-types = "0.3.1" 28 | ruint-macro = "1.1.0" 29 | keccak-const = "0.2.0" 30 | lol_alloc = "0.4.0" 31 | const-hex = { version = "1.10.0", features = ["alloc"] } 32 | -------------------------------------------------------------------------------- /pkg/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Fluidity Labs"] 3 | -------------------------------------------------------------------------------- /pkg/config.mk: -------------------------------------------------------------------------------- 1 | 2 | REPO := app.superposition.so-contracts 3 | 4 | FILES_RUST := \ 5 | $(shell find \ 6 | -path ./target -prune \ 7 | -or -name '*.rs' -print \ 8 | -or -name '*.toml' -print) 9 | 10 | FILES_SOLIDITY := \ 11 | $(shell find \ 12 | -path ./out -prune \ 13 | -or -name '*.sol' -print) 14 | 15 | OUT_SEAWATER_ADMIN := seawater-admin.wasm 16 | OUT_SEAWATER_POSITIONS := seawater-positions.wasm 17 | OUT_SEAWATER_UPDATE_POSITIONS := seawater-update-positions.wasm 18 | OUT_SEAWATER_SWAPS := seawater-swaps.wasm 19 | OUT_SEAWATER_SWAP_PERMIT2 := seawater-swap-permit2.wasm 20 | OUT_SEAWATER_QUOTES := seawater-quotes.wasm 21 | OUT_SEAWATER_MIGRATIONS := seawater-migrations.wasm 22 | 23 | OUT_LEO := leo.wasm 24 | 25 | OUT_SEAWATER_AMM := out/SeawaterAMM.sol/SeawaterAMM.json 26 | OUT_OWNERSHIP_NFTS := out/OwnershipNFTs.sol/OwnershipNFTs.json 27 | -------------------------------------------------------------------------------- /pkg/deploy-seawater.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eu 2 | 3 | wasm_file="$1" 4 | 5 | cargo stylus deploy \ 6 | --endpoint $STYLUS_ENDPOINT \ 7 | --wasm-file "$wasm_file" \ 8 | --private-key $STYLUS_PRIVATE_KEY \ 9 | | sed -nr 's/.*deployed code at address: +.*(0x.{40}).*$/\1/p' 10 | -------------------------------------------------------------------------------- /pkg/deploy-solidity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -ue 2 | 3 | name="$1" 4 | 5 | shift 6 | 7 | forge create "$name" \ 8 | --json \ 9 | --rpc-url="$STYLUS_ENDPOINT" \ 10 | --private-key="$STYLUS_PRIVATE_KEY" \ 11 | $@ \ 12 | | jq -r .deployedTo 13 | -------------------------------------------------------------------------------- /pkg/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "sol" 3 | out = "out" 4 | libs = ["lib"] 5 | via_ir = true 6 | 7 | [doc] 8 | out = "docs-out" 9 | title = "Longtail AMM contracts docs" 10 | repository = "https://github.com/fluidity-money/long.so" 11 | 12 | revert-strings = "debug" 13 | 14 | remappings = ["@opensezppling/=lib/openzeppelin-contracts/"] 15 | -------------------------------------------------------------------------------- /pkg/koko/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Superposition CLOB (Koko) 3 | -------------------------------------------------------------------------------- /pkg/leo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "leo" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.81" 6 | 7 | [lib] 8 | name = "libleo" 9 | edition = "2021" 10 | 11 | [[bin]] 12 | name = "leo" 13 | path = "src/main.rs" 14 | 15 | [dependencies] 16 | stylus-sdk = { workspace = true } 17 | alloy-sol-types.workspace = true 18 | ruint.workspace = true 19 | ruint-macro.workspace = true 20 | thiserror.workspace = true 21 | tiny-keccak.workspace = true 22 | lol_alloc.workspace = true 23 | keccak-const.workspace = true 24 | const-hex.workspace = true 25 | num-traits.workspace = true 26 | 27 | [dev-dependencies] 28 | rand = "0.8.5" 29 | proptest = "1.5.0" 30 | const-hex.workspace = true 31 | 32 | [features] 33 | export-abi = ["stylus-sdk/export-abi"] 34 | testing = [] 35 | -------------------------------------------------------------------------------- /pkg/leo/proptest-regressions/lib.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 71228b2e0e0fccb0da8dbfd36da15ac43f7631dc7cc763f6c76755cf85805439 # shrinks to mut tick_lower = 0, mut tick_upper = 0, per_second = [0, 0, 0, 0], starting_pool = [0, 0, 0, 0], expected_ending = 0, secs_in = 0 8 | cc 2719ce4e8dd650242243946735dce582002bb653dec90ef910342eb1865ff915 # shrinks to mut tick_lower = 0, mut tick_upper = 0, per_second = 3, starting_pool = [0, 0, 0, 1], expected_starting = 0, expected_ending = 8589230820911550892, secs_in = 1, position_lp = [0, 0, 0, 1], other_position_lp = [0, 0, 0, 0] 9 | -------------------------------------------------------------------------------- /pkg/leo/src/calldata.rs: -------------------------------------------------------------------------------- 1 | use stylus_sdk::alloy_primitives::{Address, U256}; 2 | 3 | pub fn write_selector(bytes: &mut [u8], selector: &[u8; 4]) { 4 | bytes[0..4].copy_from_slice(&selector[..]) 5 | } 6 | pub fn write_address(bytes: &mut [u8], slot: usize, address: Address) { 7 | bytes[4 + 32 * slot + 12..4 + 32 * slot + 32].copy_from_slice(&address.0 .0) 8 | } 9 | pub fn write_u256(bytes: &mut [u8], slot: usize, uint: U256) { 10 | bytes[4 + 32 * slot..4 + 32 * slot + 32].copy_from_slice(&uint.to_be_bytes::<32>()) 11 | } 12 | -------------------------------------------------------------------------------- /pkg/leo/src/erc20.rs: -------------------------------------------------------------------------------- 1 | use stylus_sdk::alloy_primitives::{Address, U256}; 2 | 3 | pub fn take(pool: Address, amount: U256) -> Result<(), Vec> { 4 | Ok(()) 5 | } 6 | 7 | pub fn give(token: Address, amount: U256) -> Result<(), Vec> { 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /pkg/leo/src/immutables.rs: -------------------------------------------------------------------------------- 1 | use stylus_sdk::alloy_primitives::Address; 2 | 3 | // test only implementation that returns a dummy value (so you can pick it from logs) 4 | #[cfg(not(target_arch = "wasm32"))] 5 | macro_rules! addr { 6 | ($_input:literal) => { 7 | // this says "fluidity_1" if you squint 8 | Address::new([ 9 | 0xf1, 0x01, 0xd1, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 10 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 11 | ]) 12 | }; 13 | } 14 | 15 | #[cfg(target_arch = "wasm32")] 16 | macro_rules! addr { 17 | ($input:literal) => { 18 | Address::new( 19 | match const_hex::const_decode_to_array::<20>(env!($input).as_bytes()) { 20 | Ok(res) => res, 21 | Err(_) => panic!(), 22 | }, 23 | ) 24 | }; 25 | } 26 | 27 | #[allow(dead_code)] 28 | pub const SEAWATER_ADDR: Address = addr!("FLU_SEAWATER_ADDR"); 29 | 30 | #[allow(dead_code)] 31 | pub const NFT_MANAGER_ADDR: Address = addr!("FLU_NFT_MANAGER_ADDR"); 32 | -------------------------------------------------------------------------------- /pkg/leo/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(target_arch = "wasm32", no_main, no_std)] 2 | 3 | use libleo::user_entrypoint as stylus_entrypoint; 4 | 5 | pub extern "C" fn user_entrypoint(len: usize) -> usize { 6 | stylus_entrypoint(len) 7 | } 8 | 9 | #[cfg(not(target_arch = "wasm32"))] 10 | #[doc(hidden)] 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /pkg/leo/src/maths.rs: -------------------------------------------------------------------------------- 1 | use stylus_sdk::alloy_primitives::U256; 2 | 3 | /// Returns `a * b / c` and if the result had carry. Copied from Seawater and Result 4 | /// removed. 5 | pub fn _mul_div(a: U256, b: U256, mut denom_and_rem: U256) -> U256 { 6 | assert!(!denom_and_rem.is_zero()); 7 | 8 | let mut mul_and_quo = a.widening_mul::<256, 4, 512, 8>(b); 9 | 10 | unsafe { 11 | ruint::algorithms::div(mul_and_quo.as_limbs_mut(), denom_and_rem.as_limbs_mut()); 12 | } 13 | 14 | let limbs = mul_and_quo.into_limbs(); 15 | assert!(limbs[4..] == [0_u64; 4]); 16 | 17 | let has_carry = denom_and_rem != U256::ZERO; 18 | 19 | U256::from_limbs_slice(&limbs[0..4]) 20 | } 21 | 22 | pub fn calc_base_rewards(pool_lp: U256, our_lp: U256, rewards_per_sec: U256) -> U256 { 23 | _mul_div(pool_lp, rewards_per_sec, our_lp) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seawater-tests", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "type": "module", 7 | "dependencies": { 8 | "@uniswap/permit2-sdk": "^1.2.0", 9 | "ethers": "^6.13.1", 10 | "tsx": "^3.13.0", 11 | "typescript": "^5.2.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pkg/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2024-07-01" 3 | -------------------------------------------------------------------------------- /pkg/seawater/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "seawater" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.81" 6 | 7 | [lib] 8 | name = "libseawater" 9 | edition = "2021" 10 | 11 | [[bin]] 12 | name = "seawater" 13 | path = "src/main.rs" 14 | 15 | [dependencies] 16 | stylus-sdk = { workspace = true } 17 | alloy-sol-types.workspace = true 18 | ruint.workspace = true 19 | ruint-macro.workspace = true 20 | thiserror.workspace = true 21 | tiny-keccak.workspace = true 22 | keccak-const.workspace = true 23 | const-hex.workspace = true 24 | num-traits.workspace = true 25 | lol_alloc.workspace = true 26 | 27 | [dev-dependencies] 28 | rand = "0.8.5" 29 | maplit = "1.0.2" 30 | proptest = "1.5.0" 31 | 32 | [features] 33 | export-abi = ["stylus-sdk/export-abi"] 34 | swaps = [] 35 | swap_permit2 = [] 36 | quotes = [] 37 | positions = [] 38 | update_positions = [] 39 | admin = [] 40 | migrations = [] 41 | debug = [] 42 | testing = [] 43 | testing-dbg = [] 44 | log-events = [] 45 | -------------------------------------------------------------------------------- /pkg/seawater/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Superposition AMM (Seawater) 3 | 4 | Check ../README.md 5 | 6 | Stylus contracts for the superposition AMM, based on 0xKitsune's port of the uniswap v3 maths to rust ([https://github.com/0xKitsune/uniswap-v3-math](https://github.com/0xKitsune/uniswap-v3-math)). 7 | -------------------------------------------------------------------------------- /pkg/seawater/proptest-regressions/maths/sqrt_price_math.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc fe5a52213d41ecb0374a41465cb052fcacce009dded2e7ebc642562500f2a72e # shrinks to min_tick = 0, upper_tick = 0, amount = 0 8 | cc 55862e4d8b70213220193c86c09be88b60bdc84b634a39b96346213d5b6218c6 # shrinks to sqrt_price_a_x_96_1 = 4294805859, sqrt_price_a_x_96_2 = 0, sqrt_price_b_x_96_1 = 4294805859, sqrt_price_b_x_96_2 = 1, amount = 1 9 | cc cfec802a992bd17661cbfe36ecafccf10d4255f85715f0f0b273d25f69cdff72 # shrinks to sqrt_price_a_x_96_1 = 4295128739, sqrt_price_a_x_96_2 = 3262002644, sqrt_price_b_x_96_1 = 4295128739, sqrt_price_b_x_96_2 = 3256827109, amount = 410048128078263078467202489002229760 10 | cc 3d0f813f67d074ce9552dbe5751161c4dbffbc0ada9d8a2036e18b281d8f328e # shrinks to sqrt_price_a_x_96_1 = 4295128739, sqrt_price_a_x_96_2 = 168603492, sqrt_price_b_x_96_1 = 4295128739, sqrt_price_b_x_96_2 = 168052836, amount = 43627463057454743081910537516220416 11 | -------------------------------------------------------------------------------- /pkg/seawater/src/erc20.rs: -------------------------------------------------------------------------------- 1 | //! ERC20 functions, including taking, sending, and taking using 2 | //! permit2. Platform-dependent and optionally mocked out, if tests are 3 | //! enabled. 4 | 5 | #[cfg(target_arch = "wasm32")] 6 | pub use crate::wasm_erc20::*; 7 | 8 | #[cfg(all(not(target_arch = "wasm32"), feature = "testing"))] 9 | pub use crate::host_erc20::*; 10 | 11 | pub use crate::permit2_types::*; 12 | -------------------------------------------------------------------------------- /pkg/seawater/src/events.rs: -------------------------------------------------------------------------------- 1 | //! Autogenerated structures for EVM events. 2 | 3 | //use stylus_sdk::alloy_sol_types::sol; 4 | 5 | //sol!("../sol/ISeawaterEvents.sol"); 6 | 7 | //pub use ISeawaterEvents::*; 8 | -------------------------------------------------------------------------------- /pkg/seawater/src/immutables.rs: -------------------------------------------------------------------------------- 1 | //! Per-deployment constants, intended to be used the same way `immutable` variables are in 2 | //! solidity 3 | 4 | use crate::types::Address; 5 | 6 | #[cfg(target_arch = "wasm32")] 7 | macro_rules! addr { 8 | ($input:literal) => { 9 | Address::new( 10 | match const_hex::const_decode_to_array::<20>(env!($input).as_bytes()) { 11 | Ok(res) => res, 12 | Err(_) => panic!(), 13 | }, 14 | ) 15 | }; 16 | } 17 | 18 | // test only implementation that returns a dummy value (so you can pick it from logs) 19 | #[cfg(not(target_arch = "wasm32"))] 20 | macro_rules! addr { 21 | ($_input:literal) => { 22 | // this says "fluidity_1" if you squint 23 | Address::new([ 24 | 0xf1, 0x01, 0xd1, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 26 | ]) 27 | }; 28 | } 29 | 30 | /// The address of the permit2 contract. 31 | pub const PERMIT2_ADDR: Address = addr!("FLU_SEAWATER_PERMIT2_ADDR"); 32 | 33 | /// The address of the fluid token, to be used as token 1 for every pool. 34 | pub const FUSDC_ADDR: Address = addr!("FLU_SEAWATER_FUSDC_ADDR"); 35 | -------------------------------------------------------------------------------- /pkg/seawater/src/main.rs: -------------------------------------------------------------------------------- 1 | //! # seawater 2 | //! 3 | //! `seawater` is the binary crate for the seawater AMM. 4 | //! 5 | //! This crate is just a thin shim around [libseawater]. `seawater` is 6 | //! implemented as a library crate to make it easier to test, but 7 | //! `cargo stylus` needs a binary crate in order to work properly. 8 | //! See that crate for docs. 9 | 10 | #![cfg_attr(target_arch = "wasm32", no_main, no_std)] 11 | 12 | use libseawater::user_entrypoint as stylus_entrypoint; 13 | 14 | /// Stylus entrypoint for `seawater` 15 | /// 16 | /// Reexport of [libseawater's entrypoint](libseawater::user_entrypoint). 17 | pub extern "C" fn user_entrypoint(len: usize) -> usize { 18 | stylus_entrypoint(len) 19 | } 20 | 21 | // for whatever reason this needs to be set or else tests won't build, even without no_main 22 | #[cfg(not(target_arch = "wasm32"))] 23 | #[doc(hidden)] 24 | fn main() {} 25 | -------------------------------------------------------------------------------- /pkg/seawater/src/maths/mod.rs: -------------------------------------------------------------------------------- 1 | //! Pure functions implementing the core uniswap v3 maths. 2 | //! 3 | //! Adapted from [0xKitsune's port](https://github.com/0xKitsune/uniswap-v3-math) of uniswap v3's 4 | //! maths to rust, with some modifications for code size optimisation. 5 | //! 6 | //! Most of these files are direct ports of uniswap v3's 7 | //! [libraries](https://github.com/Uniswap/v3-core/tree/main/contracts/libraries). 8 | 9 | pub mod bit_math; 10 | pub mod full_math; 11 | pub mod liquidity_math; 12 | pub mod sqrt_price_math; 13 | pub mod swap_math; 14 | pub mod tick_bitmap; 15 | pub mod tick_math; 16 | pub mod unsafe_math; 17 | pub mod utils; 18 | -------------------------------------------------------------------------------- /pkg/seawater/src/maths/unsafe_math.rs: -------------------------------------------------------------------------------- 1 | //! Unchecked maths operations. 2 | 3 | use crate::types::{U256Extension, U256}; 4 | 5 | /// Returns `a / b`, rounded up. 6 | pub fn div_rounding_up(a: U256, b: U256) -> U256 { 7 | let (quotient, remainder) = a.div_rem(b); 8 | if remainder.is_zero() { 9 | quotient 10 | } else { 11 | quotient + U256::one() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pkg/seawater/src/maths/utils.rs: -------------------------------------------------------------------------------- 1 | //! Various constants, used mostly in tests. 2 | use crate::types::U256; 3 | 4 | /// 0 as a 256 bit integer. 5 | pub const RUINT_ZERO: U256 = U256::ZERO; 6 | /// 1 as a 256 bit integer. 7 | pub const RUINT_ONE: U256 = U256::from_limbs([1, 0, 0, 0]); 8 | /// 2 as a 256 bit integer. 9 | pub const RUINT_TWO: U256 = U256::from_limbs([2, 0, 0, 0]); 10 | /// 3 as a 256 bit integer. 11 | pub const RUINT_THREE: U256 = U256::from_limbs([3, 0, 0, 0]); 12 | /// The largest 256 bit unsigned integer representable. 13 | pub const RUINT_MAX_U256: U256 = U256::from_limbs([ 14 | 18446744073709551615, 15 | 18446744073709551615, 16 | 18446744073709551615, 17 | 18446744073709551615, 18 | ]); 19 | -------------------------------------------------------------------------------- /pkg/seawater/src/permit2_types.rs: -------------------------------------------------------------------------------- 1 | use crate::types::U256; 2 | 3 | #[derive(Debug)] 4 | pub struct Permit2Args<'a> { 5 | pub max_amount: U256, 6 | pub nonce: U256, 7 | pub deadline: U256, 8 | pub sig: &'a [u8], 9 | } 10 | -------------------------------------------------------------------------------- /pkg/seawater/src/test_shims.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(not(target_arch = "wasm32"), feature = "testing"))] 2 | pub use crate::host_test_shims::*; 3 | -------------------------------------------------------------------------------- /pkg/seawater/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(not(target_arch = "wasm32"), feature = "testing"))] 2 | pub use crate::host_test_utils::*; 3 | -------------------------------------------------------------------------------- /pkg/seawater/tests/reference/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod full_math; 2 | pub mod tick_math; 3 | -------------------------------------------------------------------------------- /pkg/seawater/tests/reference_impls.proptest-regressions: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 49ac7ba6304f932478fb674b3eb182a9e4151e906a610acf85195c8826b31337 # shrinks to a = 1, b = 1 8 | cc 5f77aed2c0cc4cffc75e6108ce64f29b40a1ae0fa6097ae622057a812559c08f # shrinks to a = 177317254630119719759439229846428932842, b = 326511059171472622784353987941573168231, uleft = false, uright = true 9 | -------------------------------------------------------------------------------- /pkg/sol/IERC721TokenReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-Identifier: CC0 2 | pragma solidity 0.8.16; 3 | 4 | interface IERC721TokenReceiver { 5 | /// @notice Handle the receipt of an NFT 6 | /// @dev The ERC721 smart contract calls this function on the recipient 7 | /// after a `transfer`. This function MAY throw to revert and reject the 8 | /// transfer. Return of other than the magic value MUST result in the 9 | /// transaction being reverted. 10 | /// Note: the contract address is always the message sender. 11 | /// @param _operator The address which called `safeTransferFrom` function 12 | /// @param _from The address which previously owned the token 13 | /// @param _tokenId The NFT identifier which is being transferred 14 | /// @param _data Additional data with no specified format 15 | /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 16 | /// unless throwing 17 | function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns (bytes4); 18 | } 19 | -------------------------------------------------------------------------------- /pkg/sol/IFaucet.sol: -------------------------------------------------------------------------------- 1 | // SPDX-Identifier: MIT 2 | 3 | pragma solidity 0.8.16; 4 | 5 | interface IFaucet { 6 | struct FaucetReq { 7 | address recipient; 8 | bool isStaker; 9 | } 10 | 11 | /** 12 | * @notice sendTo the recipients given, with the amount being randomly chosen. 13 | * @dev will break if there's not enough to send... by design. 14 | * @dev will break if it tries to send to a contract. The callee should verify that 15 | * they're not being asked to send to contracts. 16 | */ 17 | function sendTo(FaucetReq[] calldata _requests) external; 18 | } 19 | -------------------------------------------------------------------------------- /pkg/sol/ILeoEvents.sol: -------------------------------------------------------------------------------- 1 | // SPDX-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | interface ILeoEvents { 5 | event CampaignBalanceUpdated( 6 | bytes8 indexed identifier, 7 | uint256 indexed newMaximum 8 | ); 9 | 10 | event CampaignCreated( 11 | bytes8 indexed identifier, 12 | address indexed pool, 13 | address indexed token, 14 | uint256 details, // [tick lower, tick upper, owner], 15 | uint256 times // [starting, ending] 16 | ); 17 | 18 | event CampaignUpdated( 19 | bytes8 indexed identifier, 20 | address indexed pool, 21 | uint256 indexed perSecond, 22 | uint256 extras // [tick lower, tick upper, starting, ending] 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /pkg/sol/ISeawater.sol: -------------------------------------------------------------------------------- 1 | // SPDX-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | import "./ISeawaterAMM.sol"; 5 | 6 | interface ISeawater is ISeawaterAMM {} 7 | -------------------------------------------------------------------------------- /pkg/sol/ISeawaterMigrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-Identifier: MIT 2 | pragma solidity 0.8.16; 3 | 4 | interface ISeawaterMigrations {} 5 | -------------------------------------------------------------------------------- /pkg/test-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | 3 | export \ 4 | STYLUS_ENDPOINT="http://localhost:8547" \ 5 | STYLUS_PRIVATE_KEY="0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" \ 6 | SEAWATER_PROXY_ADMIN="0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E" 7 | 8 | export FLU_SEAWATER_FUSDC_ADDR="$(\ 9 | sh deploy-solidity.sh "LightweightERC20" \ 10 | --constructor-args \ 11 | "Fluid-USDC" \ 12 | "fUSDC" \ 13 | 6 \ 14 | 100000000000000000000 \ 15 | "$SEAWATER_PROXY_ADMIN")" 16 | 17 | ./deploy.sh 18 | -------------------------------------------------------------------------------- /pkg/test/FaucetTest.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/pkg/test/FaucetTest.bin -------------------------------------------------------------------------------- /pkg/tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | export \ 4 | SPN_GETH_URL=http://localhost:8547 \ 5 | RUST_BACKTRACE=1 6 | 7 | cargo test --package seawater --features testing 8 | -------------------------------------------------------------------------------- /pkg/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./ethers-tests/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/remappings.txt -------------------------------------------------------------------------------- /scope.txt: -------------------------------------------------------------------------------- 1 | ./pkg/sol/OwnershipNFTs.sol 2 | ./pkg/sol/SeawaterAMM.sol 3 | ./pkg/seawater/src/main.rs 4 | ./pkg/seawater/src/maths/bit_math.rs 5 | ./pkg/seawater/src/maths/full_math.rs 6 | ./pkg/seawater/src/maths/liquidity_math.rs 7 | ./pkg/seawater/src/maths/mod.rs 8 | ./pkg/seawater/src/maths/sqrt_price_math.rs 9 | ./pkg/seawater/src/maths/swap_math.rs 10 | ./pkg/seawater/src/maths/tick_bitmap.rs 11 | ./pkg/seawater/src/maths/tick_math.rs 12 | ./pkg/seawater/src/maths/unsafe_math.rs 13 | ./pkg/seawater/src/maths/utils.rs 14 | ./pkg/seawater/src/wasm_erc20.rs 15 | ./pkg/seawater/src/types.rs 16 | ./pkg/seawater/src/tick.rs 17 | ./pkg/seawater/src/position.rs 18 | ./pkg/seawater/src/lib.rs 19 | ./pkg/seawater/src/immutables.rs 20 | ./pkg/seawater/src/host_erc20.rs 21 | ./pkg/seawater/src/events.rs 22 | ./pkg/seawater/src/eth_serde.rs 23 | ./pkg/seawater/src/erc20.rs 24 | ./pkg/seawater/src/error.rs 25 | ./pkg/seawater/src/permit2_types.rs 26 | ./pkg/seawater/src/pool.rs 27 | -------------------------------------------------------------------------------- /static/embed.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c6d1ad1b021910b1b8a9d8556299d5e65c8754672c5230026bce8a62f8fc26c4 3 | size 1542036 4 | -------------------------------------------------------------------------------- /static/empty: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 3 | size 3 4 | -------------------------------------------------------------------------------- /tools/ethereum-selector-mine.go: -------------------------------------------------------------------------------- 1 | // ethereum-selector-mine: mine permutations of the function given of the 2 | // kind (_[0-9a-zA-Z]+), until it corresponds to a magic byte that we 3 | // want for the first character of the function's selector. Takes characters 4 | // and converts them to upper (thanks Stylus...) 5 | 6 | package main 7 | 8 | import ( 9 | "bytes" 10 | "encoding/hex" 11 | "fmt" 12 | "math/rand" 13 | "os" 14 | "runtime" 15 | "strconv" 16 | "strings" 17 | "time" 18 | 19 | ethCrypto "github.com/ethereum/go-ethereum/crypto" 20 | ) 21 | 22 | func main() { 23 | sig := os.Args[1] 24 | if sig == "" { 25 | return 26 | } 27 | desired_, err := strconv.Atoi(os.Args[2]) 28 | if err != nil { 29 | panic(err) 30 | } 31 | desired := byte(desired_) 32 | p := strings.Index(sig, "(") 33 | done := make(chan string) 34 | for i := 0; i < runtime.NumCPU(); i++ { 35 | i := i 36 | go func() { 37 | r := rand.New(rand.NewSource(time.Now().Unix() + int64(i))) 38 | for { 39 | b := make([]byte, 4) 40 | if _, err := r.Read(b); err != nil { 41 | panic(err) 42 | } 43 | s := strings.ToUpper(hex.EncodeToString(b)) 44 | f := string(sig[:p]) + s + string(sig[p:]) 45 | k := ethCrypto.Keccak256([]byte(f)) 46 | if bytes.Compare(k[:2], []byte{0, 0}) == 0 { 47 | if k[2] == desired { 48 | done <- f 49 | } 50 | } 51 | } 52 | }() 53 | } 54 | fmt.Println(<-done) 55 | } 56 | -------------------------------------------------------------------------------- /web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "next/core-web-vitals", 5 | "prettier", 6 | "plugin:tailwindcss/recommended" 7 | ], 8 | "plugins": ["@stylistic/ts"], 9 | "parser": "@typescript-eslint/parser", 10 | "overrides": [ 11 | { 12 | "files": ["*.ts", "*.tsx"], 13 | "processor": "@graphql-eslint/graphql" 14 | }, 15 | { 16 | "files": ["*.graphql"], 17 | "parser": "@graphql-eslint/eslint-plugin", 18 | "plugins": ["@graphql-eslint"] 19 | }, 20 | { 21 | "files": "__tests__/**", 22 | "extends": "plugin:jest/recommended" 23 | }, 24 | { 25 | "files": "e2e/**", 26 | "extends": "plugin:playwright/recommended" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | .nyc_output 10 | /test-results 11 | /.v8-coverage 12 | /jest-coverage 13 | /playwright-coverage 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | .zed 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # local env files 33 | .env*.local 34 | 35 | # vercel 36 | .vercel 37 | 38 | # typescript 39 | *.tsbuildinfo 40 | next-env.d.ts 41 | 42 | out 43 | 44 | # Sentry Config File 45 | .env.sentry-build-plugin 46 | coverage 47 | -------------------------------------------------------------------------------- /web/.nvmrc: -------------------------------------------------------------------------------- 1 | 20.15.0 -------------------------------------------------------------------------------- /web/.prettierignore: -------------------------------------------------------------------------------- 1 | src/gql 2 | src/lib/abi -------------------------------------------------------------------------------- /web/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | FROM alpine:3.19.1 AS build 3 | 4 | RUN apk add --no-cache \ 5 | nodejs \ 6 | npm \ 7 | yarn 8 | 9 | WORKDIR /usr/local/src/superposition/web 10 | 11 | COPY package.json . 12 | 13 | RUN ls 14 | 15 | RUN pnpm i 16 | 17 | COPY . . 18 | 19 | RUN pnpm run build 20 | 21 | FROM nginx:stable-alpine3.19 AS runner 22 | 23 | COPY --from=build /usr/local/src/superposition/web /var/www/html 24 | 25 | RUN echo "server {listen 80 default_server;listen [::]:80 default_server;root /var/www/html;index index.html;server_name long.so;location / {try_files $uri /index.html;}}" > /etc/nginx/http.d/default.conf 26 | -------------------------------------------------------------------------------- /web/Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: clean docker 3 | 4 | docker: 5 | @docker build -t superposition/long.so . 6 | 7 | clean: 8 | @rm -f node_modules/ out/ 9 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Longtail AMM webapp 3 | 4 | ## Getting Started 5 | 6 | First, run the development server: 7 | 8 | pnpm dev 9 | 10 | ## Code generation with GraphQL 11 | 12 | pnpm run codegen 13 | 14 | ## Environment variables 15 | 16 | | Environment variable | Description | 17 | |-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| 18 | | `NEXT_PUBLIC_LONGTAIL_GRAPHQL_URL` | Location of the GraphQL URL for executing queries/migrations against. Should be set to https://testnet-graph.long.so for testnet production data. | 19 | | `LONGTAIL_GRAPHQL_SCHEMA` | Real path to the GraphQL schema file found in the cmd/graphql.ethereum directory. Used for codegen. | 20 | | `NEXT_PUBLIC_LONGTAIL_WALLETCONNECT_PROJECT_ID` | Walletconnect project ID that's needed to build the frontend. | 21 | -------------------------------------------------------------------------------- /web/__tests__/01_lib_math/001_sqrt_price.test.ts: -------------------------------------------------------------------------------- 1 | import { encodeSqrtPrice, sqrtPriceX96ToPrice } from "@/lib/math"; 2 | 3 | describe("encodeSqrtPrice", () => { 4 | it("Should work with 0.03437261 tick.", () => { 5 | const ethTick = BigInt(14688783812173476777496150016); 6 | expect(encodeSqrtPrice(0.03437261)).toEqual(ethTick); 7 | }); 8 | }); 9 | 10 | describe("sqrtPriceX96ToPrice", () => { 11 | it("18 decimals, price > 1", () => { 12 | const decimals = 18; 13 | const sqrtPriceX96 = 1082626999771884967498373611162n; 14 | const encoded = sqrtPriceX96ToPrice(sqrtPriceX96, decimals); 15 | expect(encoded).toBe(186723311178398592718n); 16 | expect(Number(encoded) / 10 ** decimals).toBe(186.7233111783986); 17 | }); 18 | it("18 decimals, price < 1", () => { 19 | const decimals = 18; 20 | const sqrtPriceX96 = 4730467712712532270754096n; 21 | const encoded = sqrtPriceX96ToPrice(sqrtPriceX96, decimals); 22 | expect(encoded).toBe(3564913510n); 23 | expect(Number(encoded) / 10 ** decimals).toBe(3.56491351e-9); 24 | }); 25 | it("6 decimals, price > 1", () => { 26 | const decimals = 6; 27 | const sqrtPriceX96 = 79255302979313818192107071359n; 28 | const encoded = sqrtPriceX96ToPrice(sqrtPriceX96, decimals); 29 | expect(encoded).toBe(1000685n); 30 | expect(Number(encoded) / 10 ** decimals).toBe(1.000685); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /web/__tests__/01_lib_math/002_delta.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getSqrtRatioAtTick, 3 | getAmountsForLiquidity, 4 | encodeSqrtPrice, 5 | MIN_TICK, 6 | MIN_SQRT_RATIO, 7 | MAX_TICK, 8 | MAX_SQRT_RATIO, 9 | } from "@/lib/math"; 10 | 11 | describe("Liquidity math", () => { 12 | it("Should get the sqrt ratio at a tick correctly", () => { 13 | // tests taken from the Uniswap math implementation 14 | expect(getSqrtRatioAtTick(50n)).toEqual(79426470787362580746886972461n); 15 | expect(getSqrtRatioAtTick(100n)).toEqual(79625275426524748796330556128n); 16 | expect(getSqrtRatioAtTick(250n)).toEqual(80224679980005306637834519095n); 17 | expect(getSqrtRatioAtTick(500n)).toEqual(81233731461783161732293370115n); 18 | expect(getSqrtRatioAtTick(1000n)).toEqual(83290069058676223003182343270n); 19 | expect(getSqrtRatioAtTick(2500n)).toEqual(89776708723587163891445672585n); 20 | expect(getSqrtRatioAtTick(3000n)).toEqual(92049301871182272007977902845n); 21 | expect(getSqrtRatioAtTick(BigInt(MIN_TICK))).toEqual(MIN_SQRT_RATIO); 22 | expect(getSqrtRatioAtTick(BigInt(MAX_TICK))).toEqual(MAX_SQRT_RATIO); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /web/__tests__/02_lib_usdFormat/001_usdFormat.test.ts: -------------------------------------------------------------------------------- 1 | import { usdFormat } from "@/lib/usdFormat"; 2 | 3 | describe("usdFormat", () => { 4 | it("decimals trim", () => { 5 | expect(usdFormat(1.0000000123)).toEqual("$1.00"); 6 | }); 7 | it("tens", () => { 8 | expect(usdFormat(10.256)).toEqual("$10.26"); 9 | }); 10 | it("hundreds", () => { 11 | expect(usdFormat(123.4568)).toEqual("$123.46"); 12 | }); 13 | it("thousands", () => { 14 | expect(usdFormat(1223.4568)).toEqual("$1,223.46"); 15 | }); 16 | it("ten thousands", () => { 17 | expect(usdFormat(19223.4568)).toEqual("$19,223.46"); 18 | }); 19 | it("hundred thousands", () => { 20 | expect(usdFormat(123223.4568)).toEqual("$123,223.46"); 21 | }); 22 | it("millions", () => { 23 | expect(usdFormat(1234000.298)).toEqual("$1.23M"); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /web/codegen.ts: -------------------------------------------------------------------------------- 1 | import type { CodegenConfig } from "@graphql-codegen/cli"; 2 | 3 | const config: CodegenConfig = { 4 | overwrite: true, 5 | schema: process.env.LONGTAIL_GRAPHQL_SCHEMA, 6 | documents: ["src/**/*.tsx", "src/**/*.ts"], 7 | ignoreNoDocuments: true, // for better experience with the watcher 8 | generates: { 9 | "src/gql/": { 10 | preset: "client", 11 | plugins: [], 12 | }, 13 | }, 14 | }; 15 | 16 | export default config; 17 | -------------------------------------------------------------------------------- /web/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "gray", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /web/e2e/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "./fixtures"; 2 | 3 | test("should navigate to the home page and welcome component should be visible", async ({ 4 | page, 5 | }) => { 6 | await page.goto("/"); 7 | 8 | await expect(page.getByTestId("welcome-component")).toBeInViewport(); 9 | }); 10 | -------------------------------------------------------------------------------- /web/e2e/fixtures.js: -------------------------------------------------------------------------------- 1 | import { test as testBase, expect } from "@playwright/test"; 2 | import { addCoverageReport } from "monocart-reporter"; 3 | 4 | // fixtures 5 | const test = testBase.extend({ 6 | autoTestFixture: [ 7 | async ({ page }, use) => { 8 | const isChromium = test.info().project.name === "Desktop Chrome"; 9 | 10 | // console.log('autoTestFixture setup...', test.info().project.name); 11 | // coverage API is chromium only 12 | if (isChromium) { 13 | await Promise.all([ 14 | page.coverage.startJSCoverage({ 15 | resetOnNavigation: false, 16 | }), 17 | page.coverage.startCSSCoverage({ 18 | resetOnNavigation: false, 19 | }), 20 | ]); 21 | } 22 | 23 | await use("autoTestFixture"); 24 | 25 | // console.log('autoTestFixture teardown...'); 26 | if (isChromium) { 27 | const [jsCoverage, cssCoverage] = await Promise.all([ 28 | page.coverage.stopJSCoverage(), 29 | page.coverage.stopCSSCoverage(), 30 | ]); 31 | const coverageList = [...jsCoverage, ...cssCoverage]; 32 | // console.log(coverageList.map((item) => item.url)); 33 | await addCoverageReport(coverageList, test.info()); 34 | } 35 | }, 36 | { 37 | scope: "test", 38 | auto: true, 39 | }, 40 | ], 41 | }); 42 | 43 | export { test, expect }; 44 | -------------------------------------------------------------------------------- /web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "tailwindcss/nesting": {}, 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /web/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:26941be74d99be55d3b1fe47f7ca461468024c55484653ed6fac35539c5fad64 3 | size 10757 4 | -------------------------------------------------------------------------------- /web/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:684cc98b70b3de82d7b1ee03e110662135f1c527c5ef39b93217df136e69abe7 3 | size 43572 4 | -------------------------------------------------------------------------------- /web/public/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:51415b476b61a95ded6efa21ac1686596544c53ac468d42fe06c135789c90fac 3 | size 9705 4 | -------------------------------------------------------------------------------- /web/public/favicon-16x16.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:83e09321943ada9e152c5be982cfa20b0d637eff334615a2509870dc158c600e 3 | size 655 4 | -------------------------------------------------------------------------------- /web/public/favicon-32x32.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:48a1377b1c5464f81ad4b88eddc804493dd9d28af241bdcee9f08939ddee8eef 3 | size 1319 4 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code-423n4/2024-08-superposition/d913ae0225584b4b42e38518788a7e38cd2f4d53/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/icons/ICON HOLE_BLACK.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e3068e56742b3aba044d8056a8fb6170b8299b6238738b0334a981f187071a24 3 | size 563 4 | -------------------------------------------------------------------------------- /web/public/icons/ICON_BLACK.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:512191070a2e86fa3ac3c979d15a7352b537beb67172090e239e1d0dbdea6d4f 3 | size 7031 4 | -------------------------------------------------------------------------------- /web/public/icons/cbux.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:42e494f2dfb9e00db97700dd084a5cdd236e1a19a5db10662af75ff1c839494d 3 | size 4190 4 | -------------------------------------------------------------------------------- /web/public/icons/ethereum-eth-logo.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:066b2a60df66fbe2c11bbf7d37201552fd27e4edca73cac4a3e7ebda3ceb2486 3 | size 1345 4 | -------------------------------------------------------------------------------- /web/public/icons/fUSDC.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d65e53833c4a976d31a7bf3c814baad3b864b5da5eb3a1966878614d6a8486f4 3 | size 28605 4 | -------------------------------------------------------------------------------- /web/public/icons/token.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6bd13491787b05094d6ccb53ca014f135ae90d95a3d9ddbc4d6eff23920046de 3 | size 18326 4 | -------------------------------------------------------------------------------- /web/public/icons/usd-coin-usdc-logo.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:913adf09fc3097cfee7f76a3226bacd52237f2ac714817d68eaf73bfc3a72109 3 | size 1696 4 | -------------------------------------------------------------------------------- /web/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /web/scripts/clean.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const dir = process.env.NODE_V8_COVERAGE; 4 | 5 | // console.log(dir, fs.existsSync(dir)) 6 | if (fs.existsSync(dir)) { 7 | console.log(`clean previous ${dir} ...`); 8 | fs.rmSync(dir, { 9 | recursive: true, 10 | force: true, 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /web/sentry.client.config.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the client. 2 | // The config you add here will be used whenever a users loads a page in their browser. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import * as Sentry from "@sentry/nextjs"; 6 | 7 | Sentry.init({ 8 | dsn: "https://9dc67a3e8bbfe9eabf70855e22f5e703@o1103433.ingest.us.sentry.io/4507458007465984", 9 | 10 | // Adjust this value in production, or use tracesSampler for greater control 11 | tracesSampleRate: 1, 12 | 13 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 14 | debug: false, 15 | 16 | replaysOnErrorSampleRate: 1.0, 17 | 18 | // This sets the sample rate to be 10%. You may want this to be 100% while 19 | // in development and sample at a lower rate in production 20 | replaysSessionSampleRate: 0.1, 21 | 22 | // You can remove this option if you're not planning to use the Sentry Session Replay feature: 23 | integrations: [ 24 | Sentry.replayIntegration({ 25 | // Additional Replay configuration goes in here, for example: 26 | maskAllText: true, 27 | blockAllMedia: true, 28 | }), 29 | ], 30 | }); 31 | -------------------------------------------------------------------------------- /web/sentry.edge.config.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on). 2 | // The config you add here will be used whenever one of the edge features is loaded. 3 | // Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally. 4 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 5 | 6 | import * as Sentry from "@sentry/nextjs"; 7 | 8 | Sentry.init({ 9 | dsn: "https://9dc67a3e8bbfe9eabf70855e22f5e703@o1103433.ingest.us.sentry.io/4507458007465984", 10 | 11 | // Adjust this value in production, or use tracesSampler for greater control 12 | tracesSampleRate: 1, 13 | 14 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 15 | debug: false, 16 | }); 17 | -------------------------------------------------------------------------------- /web/sentry.server.config.ts: -------------------------------------------------------------------------------- 1 | // This file configures the initialization of Sentry on the server. 2 | // The config you add here will be used whenever the server handles a request. 3 | // https://docs.sentry.io/platforms/javascript/guides/nextjs/ 4 | 5 | import * as Sentry from "@sentry/nextjs"; 6 | 7 | Sentry.init({ 8 | dsn: "https://9dc67a3e8bbfe9eabf70855e22f5e703@o1103433.ingest.us.sentry.io/4507458007465984", 9 | 10 | // Adjust this value in production, or use tracesSampler for greater control 11 | tracesSampleRate: 1, 12 | 13 | // Setting this option to true will print useful information to the console while you're setting up Sentry. 14 | debug: false, 15 | 16 | // Uncomment the line below to enable Spotlight (https://spotlightjs.com) 17 | // spotlight: process.env.NODE_ENV === 'development', 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /web/src/app/PopulateQueryCache.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | // 3 | 4 | import { useEffect } from "react"; 5 | import { queryClient } from "@/context"; 6 | import { AllDataQuery } from "@/gql/graphql"; 7 | 8 | /** 9 | * This component is used to populate the query cache with the data fetched from the server. 10 | * To use this fetch the data from the server in a server component and pass it to this component. 11 | */ 12 | export default function PopulateQueryCache({ 13 | data, 14 | featuresData, 15 | }: { 16 | data: AllDataQuery; 17 | featuresData: any; 18 | }) { 19 | useEffect(() => { 20 | // using the same query key as in useGraphql.tsx 21 | queryClient.setQueryData(["graphql", ""], data); 22 | }, [data]); 23 | 24 | useEffect(() => { 25 | // using the same query key as in useFeatureFlag.tsx 26 | queryClient.setQueryData(["featureFlags"], featuresData); 27 | }, [featuresData]); 28 | 29 | return null; 30 | } 31 | -------------------------------------------------------------------------------- /web/src/app/Provider.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | import Web3ModalProvider from "@/context"; 3 | import { Toaster } from "@/components/ui/toaster"; 4 | 5 | /** 6 | * Providers which wrap the entire application 7 | */ 8 | export function Provider({ children }: { children: React.ReactNode }) { 9 | return ( 10 | 11 | {children} 12 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /web/src/app/SwapButton.tsx: -------------------------------------------------------------------------------- 1 | import Swap from "@/assets/icons/Swap.svg"; 2 | import { motion } from "framer-motion"; 3 | 4 | export const SwapButton = ({ onClick }: { onClick?: () => void }) => ( 5 | 25 | 26 | 27 | ); 28 | -------------------------------------------------------------------------------- /web/src/app/WelcomeGradient.tsx: -------------------------------------------------------------------------------- 1 | import { useWelcomeStore } from "@/stores/useWelcomeStore"; 2 | 3 | export const WelcomeGradient = () => { 4 | const { setWelcome, welcome, setHovering } = useWelcomeStore(); 5 | 6 | if (!welcome) return null; 7 | 8 | return ( 9 |
10 |
setHovering(true)} 13 | onMouseLeave={() => setHovering(false)} 14 | onClick={() => setWelcome(false)} 15 | /> 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /web/src/app/_layout/DemoData.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useFeatureFlag } from "@/hooks/useFeatureFlag"; 4 | 5 | /** 6 | * Show a banner indicating that the data is demo data. 7 | */ 8 | export const DemoData = () => { 9 | const showDemoData = useFeatureFlag("ui show demo data"); 10 | 11 | if (!showDemoData) return null; 12 | 13 | return
DEMO DATA
; 14 | }; 15 | -------------------------------------------------------------------------------- /web/src/app/_layout/NavigationMenu.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Menu from "@/components/Menu"; 4 | import { usePathname, useRouter } from "next/navigation"; 5 | import { useSwapPro } from "@/stores/useSwapPro"; 6 | 7 | /** 8 | * The main Swap/Stake navigation menu. 9 | */ 10 | export const NavigationMenu = () => { 11 | const pathname = usePathname(); 12 | const router = useRouter(); 13 | 14 | const { swapPro } = useSwapPro(); 15 | 16 | return ( 17 | 18 | { 20 | router.push("/"); 21 | }} 22 | selected={pathname === "/" || pathname.startsWith("/swap")} 23 | proToggle 24 | > 25 |
26 | Swap{" "} 27 | {swapPro &&
{" Pro"}
} 28 |
29 |
30 | { 33 | router.push("/stake"); 34 | }} 35 | selected={pathname.startsWith("/stake")} 36 | > 37 |
Stake
38 |
39 |
40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /web/src/app/global-error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as Sentry from "@sentry/nextjs"; 4 | import NextError from "next/error"; 5 | import { useEffect } from "react"; 6 | 7 | export default function GlobalError({ 8 | error, 9 | }: { 10 | error: Error & { digest?: string }; 11 | }) { 12 | useEffect(() => { 13 | Sentry.captureException(error); 14 | }, [error]); 15 | 16 | return ( 17 | 18 | 19 | {/* `NextError` is the default Next.js error page component. Its type 20 | definition requires a `statusCode` prop. However, since the App Router 21 | does not expose status codes for errors, we simply pass 0 to render a 22 | generic error message. */} 23 | 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /web/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { Welcome } from "@/app/Welcome"; 2 | 3 | import { SwapForm } from "@/components/SwapForm"; 4 | import { SwapPro } from "@/components/SwapPro"; 5 | 6 | export default function Swap() { 7 | return ( 8 |
9 |
10 | 11 | 12 |
13 | 14 | 15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /web/src/app/stake/pool/add-liquidity/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { StakeForm } from "@/components/StakeForm"; 4 | import { useSearchParams } from "next/navigation"; 5 | 6 | export default function CreatePoolPage() { 7 | const params = useSearchParams(); 8 | const positionId = Number(params.get("positionId")); 9 | const poolId = params.get("id"); 10 | 11 | return ( 12 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /web/src/app/stake/pool/confirm-liquidity/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ConfirmStake } from "@/components/ConfirmStake"; 4 | import { useSearchParams } from "next/navigation"; 5 | 6 | export default function ConfirmAddLiquidity() { 7 | const params = useSearchParams(); 8 | const positionId = Number(params.get("positionId")); 9 | 10 | return ; 11 | } 12 | -------------------------------------------------------------------------------- /web/src/app/stake/pool/create/confirm/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ConfirmStake } from "@/components/ConfirmStake"; 4 | 5 | export default function ConfirmCreatePool() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /web/src/app/stake/pool/create/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { StakeForm } from "@/components/StakeForm"; 4 | import { useSearchParams } from "next/navigation"; 5 | 6 | export default function CreatePoolPage() { 7 | const params = useSearchParams(); 8 | const poolId = params.get("id"); 9 | 10 | return ; 11 | } 12 | -------------------------------------------------------------------------------- /web/src/app/swap/confirm/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ConfirmSwap } from "@/components/ConfirmSwap"; 4 | 5 | export default function ConfirmMakeSwap() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /web/src/app/swap/inventory/page.tsx: -------------------------------------------------------------------------------- 1 | import { InventoryContent } from "@/components/InventoryContent"; 2 | 3 | export default function InvetoryPage() { 4 | return ( 5 |
6 |
11 | 12 |
13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /web/src/assets/gifs/congratulations.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:44fe5a25d70dcfc11e0e1de106b9d307bd198dcdfa2a7a46b00b724c7c4af3ab 3 | size 5248825 4 | -------------------------------------------------------------------------------- /web/src/assets/gifs/fail.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:54979d8085f8810951d2b7807e27d1f6c008bd60216bfc5ae44c689b35c940b5 3 | size 1745828 4 | -------------------------------------------------------------------------------- /web/src/assets/gifs/pending.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5c7b763a429699688ffa7887360fa3ffd68c260d933284deef461c25899a1f8d 3 | size 12430134 4 | -------------------------------------------------------------------------------- /web/src/assets/gifs/processing.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fa990e4631832c26a98be8baa25de22dc426716e027fe89017277b90b3dc73f6 3 | size 3665912 4 | -------------------------------------------------------------------------------- /web/src/assets/gifs/success.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1d909c3d9a1be7c44136ea8a71df81dafd49b0f438a6c93bf53534935ba9104d 3 | size 1770026 4 | -------------------------------------------------------------------------------- /web/src/assets/gifs/unlock.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6ac81be46e594e11352c444f905a5476a561f4d78711ee785b70b3fa6722a0ad 3 | size 807171 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/ARB.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:738421b0c463491dbffcdddff4c4b8af56f87497b91284cd922f66fe53b8a9a6 3 | size 2455 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/Caret.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a18708977480207777f2620dee2fa2ca0f7d26b9de96672582b53f8080ac3fed 3 | size 251 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/ETH.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:196d238af84b9b0e0eb03d6ded441801b6c64853963df5f796443e1e533be8ed 3 | size 1733 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/SPN.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2a12b305af29cf15d62555a8612022d79a97610ee648e4c82a5267701edefe8b 3 | size 585 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/Search.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8fd128fb9eca036ae496dd1b84b84a78fb640a442eef9626821441a1f5e81ec7 3 | size 294 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/Swap.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:614a2ec380cd5b753747061c2a13e576082cbb19704ab8a17036d11beb152f6a 3 | size 269 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/USDC.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1b00244a4fe765a7e494e40bc4f2ba4675cd7c271698a3efecfcccbf97d3f624 3 | size 6110 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/allow-swapping.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b67e5343097501a4a225ec1aee4ba7d603c9ec1275cc0a50030970d0a8986e6c 3 | size 87908 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/arrow-down-white.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c5160136e789502ff519265c5fa72709cd3dcef79843aa22c3f63e7538546345 3 | size 927 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:957429e3a1a7a208caa48d5361adb12d764b380795bd5e9dad578ac26f4e75ce 3 | size 562 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/arrow-up-right.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fea636ff425774cccc02ab41c30b281885314410d153e5b41b75b70082ecb161 3 | size 271 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/circles.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4f035311afda0755a2713a6f5f3a12a9b60ef203d6a32ee7cf5e5148fd0f1856 3 | size 432 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/cog.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:63450a075a6b813c21fa429bed40f090b360fb47fe99952f9c8858c436beb0e2 3 | size 3750 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/disconnect.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:04c92068f3d0dbb123f72a92f3d554a5e6b157126bb28dc76263c62911c68387 3 | size 1377 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/discord.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6817c341113d428cd906203d19a041003cee833c244cf94762ca9c3ebf4177d3 3 | size 925 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/ethereum.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b91a31a6bac4bc681cb879c0e9e3e167de82d4b2f52f3ec99cca1f8e9223f373 3 | size 19778 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/gas.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f71276fd073f8ac434633f334a5a8ec4bc87a0ef641efe90998c57f14d3c8c35 3 | size 1841 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/grid.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:006656054b2f4eb3b747d2d04228a294eb42357e7337af5396aa34cafcfbb1f3 3 | size 469 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/hourglass.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:550367e4f051f2c7ba152eacc2d7ed84c91b5d29357952079fd27a3985cc2f1a 3 | size 3955 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/iridescent-pickaxe-2.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c4b8d850247cf8343e63e4ab76aef47087c880a14c2e89a945525499c8698915 3 | size 4582 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/iridescent-pickaxe.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:17c61896c0ba808d3e3390361514c49699d9695bf9e1c27d83e3f0fe0076726b 3 | size 3030 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/iridescent-token.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8d0a8babdda9e079bd221c5d641d727f46904b71e44a02085f611f0263598f33 3 | size 7141 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/legend/current-price.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8f67bea3e18a5c1a44fa31167e283dd6a155dea17dcc95b5455ceb64a35b14a8 3 | size 157 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/legend/liquidity-distribution.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1991c985e870138a6e42992635ac0e4c87590bedc91e10270675e8c112519424 3 | size 266 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/legend/selected-range.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6c9ec39830a1c37fbcfc60ffcd0378fbb5bf95c33d262debaa9c2b2d538a2cf0 3 | size 157 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/list.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:88504e724bcd788dfafb49ca3fc9e98521ab4e6a3cd8d18d7780803da29e3a2f 3 | size 448 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/long-tail.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7d7b82b05f1b6d804723f532cae2c5d159370ed6a3ecf605a735c353507971d6 3 | size 765 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/padlock.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e82461523623a2d31e6c445c12162348986cd318cd5e1692a0616560082e1897 3 | size 1259 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/position.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1fd95a90137b21ad3402d19b2801e427cf72a59088d793120ff361138b886bb7 3 | size 300 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/pro-toggle-selected.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a0d784fa1dc5f596630de3233c66203247ac12d86e385d7dbde00d0307a2fe01 3 | size 2960 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/pro-toggle.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8788431c25e837fd79950f2e7a7fe106410c5a500f72861742996fb13e97dc1d 3 | size 2850 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/profile-picture.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:83e6c6baecde588b36c8678f6dc910ee53fdf96ed3577a8548ac9aa661d9d1a5 3 | size 47766 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/sort.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2796df312a573f01768eca56ec7326b9879be9f150159d5b43e18a247a05812c 3 | size 968 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/spn-test.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:31bb5ee44f4b6b238af98b3b22fec90918a0fa1bb69a3151b3e4aa86fd4d8767 3 | size 18930 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/success.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1d909c3d9a1be7c44136ea8a71df81dafd49b0f438a6c93bf53534935ba9104d 3 | size 1770026 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/superposition.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f2baab1a179ef2558b26d67e59a23a8a933950cadf20bb4a5b8692a17de0a847 3 | size 530 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/token-borderless.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4553060f79333a049e5e1c8b1205c7734cd0744a19a007dfa3e6bdb56cf73611 3 | size 6176 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/token-iridescent.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2fae8c3e8bf0a39af89bafcaaeeb7f7963dbef2d8c09fe5688ff4f943cd199f1 3 | size 25271 4 | -------------------------------------------------------------------------------- /web/src/assets/icons/token.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6bd13491787b05094d6ccb53ca014f135ae90d95a3d9ddbc4d6eff23920046de 3 | size 18326 4 | -------------------------------------------------------------------------------- /web/src/assets/profile-picture.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6db3b262a4a54ffa1eb60878196f2bd2ba0b746acf0d74a288386114d91da5ff 3 | size 35370 4 | -------------------------------------------------------------------------------- /web/src/components/DurationSegmentedControl.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import SegmentedControl, { 3 | SegmentedControlProps, 4 | } from "@/components/ui/segmented-control"; 5 | 6 | export const DurationSegmentedControl = ({ 7 | className, 8 | variant, 9 | callback, 10 | }: { 11 | className?: string; 12 | variant?: SegmentedControlProps["variant"]; 13 | callback?: (value: "7D" | "1M" | "6M" | "1Y" | "ALL") => void; 14 | }) => { 15 | return ( 16 | 49 | ); 50 | }; 51 | -------------------------------------------------------------------------------- /web/src/components/InventoryContent/DisconnectButton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import Disconnect from "@/assets/icons/disconnect.svg"; 3 | import { Badge } from "@/components/ui/badge"; 4 | import { useState } from "react"; 5 | import { useDetectClickOutside } from "react-detect-click-outside"; 6 | import { useDisconnect } from "wagmi"; 7 | 8 | export const DisconnectButton = () => { 9 | const [confirmDisconnect, setConfirmDisconnect] = useState(false); 10 | 11 | const ref = useDetectClickOutside({ 12 | onTriggered: () => setConfirmDisconnect(false), 13 | }); 14 | 15 | const { disconnect } = useDisconnect(); 16 | 17 | return ( 18 | { 29 | if (confirmDisconnect) { 30 | disconnect(); 31 | } else { 32 | setConfirmDisconnect(true); 33 | } 34 | }} 35 | > 36 | 41 | {confirmDisconnect && "Disconnect"} 42 | 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /web/src/components/InventoryContent/columns.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ColumnDef } from "@tanstack/react-table"; 4 | import { Button } from "@/components/ui/button"; 5 | import { Badge } from "@/components/ui/badge"; 6 | import { usdFormat } from "@/lib/usdFormat"; 7 | import Token from "@/assets/icons/token.svg"; 8 | 9 | export type Yield = { 10 | id: string; 11 | yield: number; 12 | pool: string; 13 | status: "claimable" | "claimed"; 14 | }; 15 | 16 | export const columns: ColumnDef[] = [ 17 | { 18 | accessorKey: "yield", 19 | header: "Yield", 20 | cell: ({ row }) => ( 21 | 22 | 23 | 24 | {usdFormat(row.original.yield)} 25 | 26 | ), 27 | }, 28 | { 29 | accessorKey: "pool", 30 | header: "Pool", 31 | cell: ({ row }) => ( 32 |
33 | 34 | 35 | {row.original.pool} 36 |
37 | ), 38 | }, 39 | { 40 | accessorKey: "status", 41 | header: "Status", 42 | cell: ({ row }) => 43 | row.original.status === "claimable" ? ( 44 |
45 | 48 |
49 | ) : ( 50 | "Claimed" 51 | ), 52 | }, 53 | ]; 54 | -------------------------------------------------------------------------------- /web/src/components/InventoryContent/data/myPositionsData.ts: -------------------------------------------------------------------------------- 1 | import { nanoid } from "nanoid"; 2 | 3 | export const myPositionsData = [ 4 | { 5 | id: nanoid(), 6 | pool: "USDC x fUSDC", 7 | yield: 183.01, 8 | position: "$10.1k", 9 | liquidityRange: "3.10k - 3.98k", 10 | }, 11 | { 12 | id: nanoid(), 13 | pool: "USDC x fUSDC", 14 | yield: 183.01, 15 | position: "$10.1k", 16 | liquidityRange: "3.10k - 3.98k", 17 | }, 18 | { 19 | id: nanoid(), 20 | pool: "USDC x fUSDC", 21 | yield: 0, 22 | position: "$20", 23 | liquidityRange: "3.10k - 3.98k", 24 | }, 25 | { 26 | id: nanoid(), 27 | pool: "USDC x fUSDC", 28 | yield: 183.01, 29 | position: "$10.1k", 30 | liquidityRange: "3.10k - 3.98k", 31 | }, 32 | { 33 | id: nanoid(), 34 | pool: "USDC x fUSDC", 35 | yield: 183.01, 36 | position: "$10.1k", 37 | liquidityRange: "3.10k - 3.98k", 38 | }, 39 | { 40 | id: nanoid(), 41 | pool: "USDC x fUSDC", 42 | yield: 0, 43 | position: "$20", 44 | liquidityRange: "3.10k - 3.98k", 45 | }, 46 | ]; 47 | -------------------------------------------------------------------------------- /web/src/components/InventoryContent/data/yieldData.ts: -------------------------------------------------------------------------------- 1 | import { Yield } from "@/components/InventoryContent/columns"; 2 | import { nanoid } from "nanoid"; 3 | 4 | export const yieldData: Yield[] = [ 5 | { 6 | id: nanoid(), 7 | yield: 12.33, 8 | status: "claimable", 9 | pool: "ETH-fUSDC", 10 | }, 11 | { 12 | id: nanoid(), 13 | yield: 12.33, 14 | status: "claimable", 15 | pool: "ETH x fUSDC", 16 | }, 17 | { 18 | id: nanoid(), 19 | yield: 12.33, 20 | status: "claimed", 21 | pool: "ETH x fUSDC", 22 | }, 23 | { 24 | id: nanoid(), 25 | yield: 12.33, 26 | status: "claimed", 27 | pool: "ETH x fUSDC", 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /web/src/components/InventoryContent/useInventorySettings.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | export const useInventorySettings = create<{ 4 | /** 5 | * True when settings are open 6 | */ 7 | settings: boolean; 8 | 9 | /** 10 | * Set the settings state 11 | */ 12 | setSettings: (settings: boolean) => void; 13 | }>((set) => ({ 14 | settings: false, 15 | setSettings: (settings: boolean) => set({ settings }), 16 | })); 17 | -------------------------------------------------------------------------------- /web/src/components/Menu/Menu.module.scss: -------------------------------------------------------------------------------- 1 | 2 | .Item { 3 | position: relative; 4 | z-index: 1; 5 | 6 | &.selected { 7 | * { 8 | box-sizing: content-box; 9 | } 10 | 11 | &.light { 12 | color: $light; 13 | } 14 | 15 | &.dark { 16 | color: $dark; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /web/src/components/SwapPro/SwapProGraphData.ts: -------------------------------------------------------------------------------- 1 | import { subDays } from "date-fns"; 2 | 3 | export const getSwapProGraphMockData = (length: number) => { 4 | const data = []; 5 | for (let i = 0; i < length; i++) { 6 | data.push({ 7 | date: subDays(new Date(), length - i), 8 | value: Math.floor(Math.random() * 1000), 9 | }); 10 | } 11 | return data; 12 | }; 13 | -------------------------------------------------------------------------------- /web/src/components/SwapPro/SwapProPoolFragment.tsx: -------------------------------------------------------------------------------- 1 | import { graphql } from "@/gql"; 2 | 3 | /** 4 | * Fragment containing the data for SwapPro 5 | */ 6 | export const SwapProPoolFragment = graphql(` 7 | fragment SwapProPoolFragment on SeawaterPool { 8 | address 9 | token { 10 | address 11 | symbol 12 | } 13 | liquidity { 14 | liquidity 15 | } 16 | priceOverTime { 17 | daily 18 | monthly 19 | } 20 | volumeOverTime { 21 | monthly { 22 | token1 { 23 | timestamp 24 | valueUsd 25 | } 26 | fusdc { 27 | timestamp 28 | valueUsd 29 | } 30 | } 31 | daily { 32 | token1 { 33 | timestamp 34 | valueUsd 35 | } 36 | fusdc { 37 | timestamp 38 | valueUsd 39 | } 40 | } 41 | } 42 | liquidityOverTime { 43 | daily { 44 | timestamp 45 | fusdc { 46 | valueUsd 47 | } 48 | } 49 | monthly { 50 | timestamp 51 | fusdc { 52 | valueUsd 53 | } 54 | } 55 | } 56 | swaps { 57 | swaps { 58 | transactionHash 59 | timestamp 60 | amountIn { 61 | valueScaled 62 | token { 63 | symbol 64 | } 65 | } 66 | amountOut { 67 | valueScaled 68 | token { 69 | symbol 70 | } 71 | } 72 | } 73 | } 74 | } 75 | `); 76 | -------------------------------------------------------------------------------- /web/src/components/TokenIcon.tsx: -------------------------------------------------------------------------------- 1 | import Image, { ImageProps } from "next/image"; 2 | 3 | interface TokenIconProps extends Omit { 4 | src?: string; 5 | } 6 | 7 | const TokenIcon = ({ src, ...props }: TokenIconProps) => ( 8 |
9 | 16 |
17 | ); 18 | 19 | export { TokenIcon }; 20 | -------------------------------------------------------------------------------- /web/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | import { cva, type VariantProps } from "class-variance-authority"; 5 | 6 | const inputVariants = cva( 7 | "border-input bg-background ring-offset-background placeholder:text-muted-foreground flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 ", 13 | "no-ring": "", 14 | }, 15 | }, 16 | defaultVariants: { 17 | variant: "default", 18 | }, 19 | }, 20 | ); 21 | 22 | export interface InputProps 23 | extends React.InputHTMLAttributes, 24 | VariantProps {} 25 | 26 | const Input = React.forwardRef( 27 | ({ className, type, variant, ...props }, ref) => { 28 | return ( 29 | 35 | ); 36 | }, 37 | ); 38 | Input.displayName = "Input"; 39 | 40 | export { Input }; 41 | -------------------------------------------------------------------------------- /web/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as LabelPrimitive from "@radix-ui/react-label"; 5 | import { cva, type VariantProps } from "class-variance-authority"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 11 | ); 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )); 24 | Label.displayName = LabelPrimitive.Root.displayName; 25 | 26 | export { Label }; 27 | -------------------------------------------------------------------------------- /web/src/components/ui/popover.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as PopoverPrimitive from "@radix-ui/react-popover"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const Popover = PopoverPrimitive.Root; 9 | 10 | const PopoverTrigger = PopoverPrimitive.Trigger; 11 | 12 | const PopoverContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 16 | 17 | 27 | 28 | )); 29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName; 30 | 31 | export { Popover, PopoverTrigger, PopoverContent }; 32 | -------------------------------------------------------------------------------- /web/src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ); 13 | } 14 | 15 | export { Skeleton }; 16 | -------------------------------------------------------------------------------- /web/src/components/ui/switch.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as SwitchPrimitives from "@radix-ui/react-switch"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const Switch = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 25 | 26 | )); 27 | Switch.displayName = SwitchPrimitives.Root.displayName; 28 | 29 | export { Switch }; 30 | -------------------------------------------------------------------------------- /web/src/components/ui/toaster.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { 4 | Toast, 5 | ToastClose, 6 | ToastDescription, 7 | ToastProvider, 8 | ToastTitle, 9 | ToastViewport, 10 | } from "@/components/ui/toast"; 11 | import { useToast } from "@/components/ui/use-toast"; 12 | 13 | export function Toaster() { 14 | const { toasts } = useToast(); 15 | 16 | return ( 17 | 18 | {toasts.map(function ({ id, title, description, action, ...props }) { 19 | return ( 20 | 21 |
22 | {title && {title}} 23 | {description && ( 24 | {description} 25 | )} 26 |
27 | {action} 28 | 29 |
30 | ); 31 | })} 32 | 33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /web/src/components/ui/tooltip.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const TooltipProvider = TooltipPrimitive.Provider; 9 | 10 | const Tooltip = TooltipPrimitive.Root; 11 | 12 | const TooltipTrigger = TooltipPrimitive.Trigger; 13 | 14 | const TooltipContent = React.forwardRef< 15 | React.ElementRef, 16 | React.ComponentPropsWithoutRef 17 | >(({ className, sideOffset = 4, ...props }, ref) => ( 18 | 27 | )); 28 | TooltipContent.displayName = TooltipPrimitive.Content.displayName; 29 | 30 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; 31 | -------------------------------------------------------------------------------- /web/src/components/ui/typography.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | export function TypographyH2({ 4 | children, 5 | className, 6 | }: { 7 | children: React.ReactNode; 8 | className?: string; 9 | }) { 10 | return ( 11 |

17 | {children} 18 |

19 | ); 20 | } 21 | 22 | export function TypographyH3({ 23 | children, 24 | className, 25 | }: { 26 | children: React.ReactNode; 27 | className?: string; 28 | }) { 29 | return ( 30 |

36 | {children} 37 |

38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /web/src/config/arbitrumStylusTestnet.ts: -------------------------------------------------------------------------------- 1 | import { arbitrumSepolia } from "wagmi/chains"; 2 | 3 | export const arbitrumStylusTestnet = { 4 | name: "Superposition Testnet", 5 | id: 98985, 6 | nativeCurrency: { name: "Superposition", symbol: "SPN", decimals: 18 }, 7 | rpcUrls: { 8 | default: { http: ["https://testnet-rpc.superposition.so"] }, 9 | public: { http: ["https://testnet-rpc.superposition.so"] }, 10 | }, 11 | blockExplorers: { 12 | default: { 13 | name: "CatScan", 14 | url: "https://testnet-explorer.superposition.so", 15 | }, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /web/src/config/graphqlEndpoint.ts: -------------------------------------------------------------------------------- 1 | export const graphqlEndpoint = process.env.NEXT_PUBLIC_LONGTAIL_GRAPHQL_URL; 2 | -------------------------------------------------------------------------------- /web/src/config/index.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { defaultWagmiConfig } from "@web3modal/wagmi/react/config"; 4 | import { http } from "viem"; 5 | import { arbitrumStylusTestnet } from "./arbitrumStylusTestnet"; 6 | 7 | // Get projectId at https://cloud.walletconnect.com 8 | export const projectId = 9 | process.env.NEXT_PUBLIC_LONGTAIL_WALLETCONNECT_PROJECT_ID; 10 | 11 | if (!projectId) throw new Error("Project ID is not defined"); 12 | 13 | const metadata = { 14 | name: "Longtail", 15 | description: "", 16 | url: "https://long.so", 17 | icons: [""], 18 | }; 19 | 20 | // Create wagmiConfig 21 | const chains = [arbitrumStylusTestnet] as const; 22 | 23 | export const config = defaultWagmiConfig({ 24 | chains, 25 | projectId, 26 | transports: { 27 | [arbitrumStylusTestnet.id]: http("https://testnet-rpc.superposition.so"), 28 | }, 29 | metadata, 30 | ssr: true, 31 | }); 32 | -------------------------------------------------------------------------------- /web/src/config/tokens.ts: -------------------------------------------------------------------------------- 1 | export type Token = { 2 | name: string; 3 | address: `0x${string}`; 4 | symbol: string; 5 | decimals: number; 6 | icon?: string; 7 | }; 8 | 9 | export const fUSDC: Token = { 10 | name: "fUSDC", 11 | address: "0xa8ea92c819463efbeddfb670fefc881a480f0115", 12 | symbol: "fUSDC", 13 | decimals: 6, 14 | icon: "/icons/fUSDC.svg", 15 | }; 16 | 17 | export const USDC: Token = { 18 | name: "USD Coin", 19 | address: "0x6437fdc89ced41941b97a9f1f8992d88718c81c5", 20 | symbol: "USDC", 21 | decimals: 6, 22 | icon: "/icons/usd-coin-usdc-logo.svg", 23 | }; 24 | 25 | export const WETH: Token = { 26 | name: "Wrapped Ethereum", 27 | address: "0xde104342b32bca03ec995f999181f7cf1ffc04d7", 28 | symbol: "WETH", 29 | decimals: 18, 30 | icon: "/icons/ethereum-eth-logo.svg", 31 | }; 32 | 33 | export const WSPN: Token = { 34 | name: "Wrapped Superposition", 35 | address: "0x22b9fa698b68bba071b513959794e9a47d19214c", 36 | symbol: "WSPN", 37 | decimals: 18, 38 | icon: "/icons/ICON_BLACK.png", 39 | }; 40 | 41 | export const CATBUX: Token = { 42 | name: "CATBUX", 43 | address: "0x36c116a8851869cf8a99b3bda0fad42453d32b99", 44 | symbol: "BUX", 45 | decimals: 18, 46 | icon: "/icons/cbux.svg", 47 | }; 48 | 49 | const allTokens: Token[] = [fUSDC, USDC, WSPN, WETH, CATBUX]; 50 | 51 | export const DefaultToken = USDC; 52 | 53 | export const getTokenFromAddress = (address_: string): Token | undefined => 54 | allTokens.find(({ address }) => address === address_); 55 | 56 | export const mockTokens: Token[] = [DefaultToken]; 57 | -------------------------------------------------------------------------------- /web/src/context/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { ReactNode } from "react"; 4 | import { config, projectId } from "@/config"; 5 | 6 | import { createWeb3Modal } from "@web3modal/wagmi/react"; 7 | 8 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 9 | 10 | import { State, WagmiProvider } from "wagmi"; 11 | 12 | // Setup queryClient 13 | export const queryClient = new QueryClient(); 14 | 15 | if (!projectId) throw new Error("Project ID is not defined"); 16 | 17 | // Create modal 18 | createWeb3Modal({ 19 | wagmiConfig: config, 20 | projectId, 21 | enableAnalytics: true, // Optional - defaults to your Cloud configuration 22 | enableOnramp: true, // Optional - false as default 23 | }); 24 | 25 | export default function Web3ModalProvider({ 26 | children, 27 | initialState, 28 | }: { 29 | children: ReactNode; 30 | initialState?: State; 31 | }) { 32 | return ( 33 | 34 | {children} 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /web/src/demoData/swapExploreAssets.ts: -------------------------------------------------------------------------------- 1 | import { mockTokens } from "@/config/tokens"; 2 | 3 | export const mockSwapExploreAssets = mockTokens.map((token) => ({ 4 | symbol: token.symbol, 5 | address: token.address, 6 | name: token.name, 7 | amount: 0.000846, 8 | amountUSD: 765.22, 9 | token, 10 | })); 11 | 12 | export const mockHighestRewarders = mockSwapExploreAssets; 13 | -------------------------------------------------------------------------------- /web/src/demoData/yieldOverTimeData.ts: -------------------------------------------------------------------------------- 1 | import { subDays } from "date-fns"; 2 | 3 | export const getMockYieldOverTimeData = (length: number) => { 4 | const data = []; 5 | for (let i = 0; i < length; i++) { 6 | data.push({ 7 | date: subDays(new Date(), length - i), 8 | uv: Math.floor(Math.random() * 1000), 9 | pv: Math.floor(Math.random() * 1000), 10 | }); 11 | } 12 | return data; 13 | }; 14 | -------------------------------------------------------------------------------- /web/src/gql/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fragment-masking"; 2 | export * from "./gql"; -------------------------------------------------------------------------------- /web/src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | // credit: https://github.com/uidotdev/usehooks/blob/main/index.js 4 | export function useDebounce(value: T, delay: number) { 5 | const [debouncedValue, setDebouncedValue] = useState(value); 6 | 7 | useEffect(() => { 8 | const handler = setTimeout(() => { 9 | setDebouncedValue(value); 10 | }, delay); 11 | 12 | return () => { 13 | clearTimeout(handler); 14 | }; 15 | }, [value, delay]); 16 | 17 | return debouncedValue; 18 | } 19 | -------------------------------------------------------------------------------- /web/src/hooks/useFeatureFlagOverride.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { FeatureFlags } from "@/hooks/useFeatureFlag"; 3 | import { persist } from "zustand/middleware"; 4 | 5 | /** 6 | * Zustand hook to override feature flags for local development. 7 | */ 8 | export const useFeatureFlagOverride = create( 9 | persist<{ 10 | override: boolean; 11 | setOverride: (value: boolean) => void; 12 | featureFlags: Partial; 13 | setFeatureFlagOverride: ( 14 | featureFlag: keyof FeatureFlags, 15 | value: boolean, 16 | ) => void; 17 | }>( 18 | (set) => ({ 19 | override: false, 20 | setOverride: (value) => set({ override: value }), 21 | featureFlags: {}, 22 | setFeatureFlagOverride: (featureFlag, value) => 23 | set((state) => ({ 24 | featureFlags: { 25 | ...state.featureFlags, 26 | [featureFlag]: value, 27 | }, 28 | })), 29 | }), 30 | { 31 | name: "feature-flag-override", 32 | }, 33 | ), 34 | ); 35 | -------------------------------------------------------------------------------- /web/src/hooks/useMediaQuery.ts: -------------------------------------------------------------------------------- 1 | import { useMediaQuery as useRrMediaQuery } from "react-responsive"; 2 | 3 | export const useMediaQuery = () => { 4 | const isLtSm = useRrMediaQuery({ 5 | query: "(max-width: 640px)", 6 | }); 7 | 8 | const isSm = useRrMediaQuery({ 9 | query: "(min-width: 640px)", 10 | }); 11 | 12 | const isMd = useRrMediaQuery({ 13 | query: "(min-width: 768px)", 14 | }); 15 | 16 | return { 17 | isSm, 18 | isLtSm, 19 | isMd, 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /web/src/instrumentation.ts: -------------------------------------------------------------------------------- 1 | export async function register() { 2 | if (process.env.NEXT_RUNTIME === "nodejs") { 3 | await import("../sentry.server.config"); 4 | } 5 | 6 | if (process.env.NEXT_RUNTIME === "edge") { 7 | await import("../sentry.edge.config"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /web/src/lib/abi/IFaucet.ts: -------------------------------------------------------------------------------- 1 | export const output = { 2 | abi: [ 3 | { 4 | type: "function", 5 | name: "claimAmount", 6 | inputs: [], 7 | outputs: [], 8 | stateMutability: "nonpayable", 9 | }, 10 | { 11 | type: "function", 12 | name: "timeUntilNextClaim", 13 | inputs: [ 14 | { 15 | name: "_recipient", 16 | type: "address", 17 | internalType: "address", 18 | }, 19 | ], 20 | outputs: [ 21 | { 22 | name: "", 23 | type: "uint256", 24 | internalType: "uint256", 25 | }, 26 | ], 27 | stateMutability: "view", 28 | }, 29 | { 30 | type: "function", 31 | name: "isMember", 32 | inputs: [ 33 | { 34 | name: "_recipient", 35 | type: "address", 36 | internalType: "address", 37 | }, 38 | ], 39 | outputs: [ 40 | { 41 | name: "", 42 | type: "bool", 43 | internalType: "bool", 44 | }, 45 | ], 46 | stateMutability: "view", 47 | }, 48 | ], 49 | } as const; 50 | -------------------------------------------------------------------------------- /web/src/lib/addresses.ts: -------------------------------------------------------------------------------- 1 | export const ammAddress = "0xE13Fec14aBFbAa5b185cFb46670A56BF072E13b1"; 2 | export const faucetAddress = "0x0000000000000000000000000000000000000000"; 3 | -------------------------------------------------------------------------------- /web/src/lib/publicClient.ts: -------------------------------------------------------------------------------- 1 | import { createPublicClient, http } from "viem"; 2 | import { arbitrumStylusTestnet } from "../config/arbitrumStylusTestnet"; 3 | 4 | export const publicClient = createPublicClient({ 5 | chain: arbitrumStylusTestnet, 6 | transport: http(), 7 | }); 8 | -------------------------------------------------------------------------------- /web/src/lib/usdFormat.tsx: -------------------------------------------------------------------------------- 1 | export const usdFormat = (value: number) => { 2 | return new Intl.NumberFormat("en-US", { 3 | style: "currency", 4 | currency: "USD", 5 | notation: value < 1000000 ? "standard" : "compact", 6 | minimumFractionDigits: 2, 7 | }).format(value); 8 | }; 9 | -------------------------------------------------------------------------------- /web/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /web/src/stores/useInventorySheet.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface InventorySheetStore { 4 | /** 5 | * Represents the current state of openness. 6 | */ 7 | isOpen: boolean; 8 | 9 | /** 10 | * Sets the state of openness. 11 | * 12 | * @param {boolean} isOpen The new state of openness. 13 | */ 14 | setIsOpen: (isOpen: boolean) => void; 15 | } 16 | 17 | /** 18 | * A store for the InventorySheet component. 19 | */ 20 | export const useInventorySheet = create((set) => ({ 21 | isOpen: false, 22 | setIsOpen: (isOpen) => set({ isOpen }), 23 | })); 24 | -------------------------------------------------------------------------------- /web/src/stores/useStakeWelcomeBackStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface WelcomeStore { 4 | /** 5 | * Whether the welcome screen is visible 6 | */ 7 | welcome: boolean; 8 | 9 | /** 10 | * Set the welcome screen visibility 11 | * @param welcome 12 | */ 13 | setWelcome: (welcome: boolean) => void; 14 | 15 | /** 16 | * Whether the yield breakdown screen is visible 17 | */ 18 | yieldBreakdown: boolean; 19 | 20 | /** 21 | * Set the yield breakdown screen visibility 22 | * @param yieldBreakdown 23 | */ 24 | setYieldBreakdown: (yieldBreakdown: boolean) => void; 25 | 26 | yieldBreakdownClaimed: boolean; 27 | setYieldBreakdownClaimed: (yieldBreakdownClaimed: boolean) => void; 28 | } 29 | 30 | export const useStakeWelcomeBackStore = create((set) => ({ 31 | welcome: false, 32 | setWelcome: (welcome) => set({ welcome }), 33 | 34 | yieldBreakdown: false, 35 | setYieldBreakdown: (yieldBreakdown) => set({ yieldBreakdown }), 36 | 37 | yieldBreakdownClaimed: false, 38 | setYieldBreakdownClaimed: (yieldBreakdownClaimed) => 39 | set({ yieldBreakdownClaimed }), 40 | })); 41 | -------------------------------------------------------------------------------- /web/src/stores/useSwapPro.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface SwapProStore { 4 | /** 5 | * Whether the swap screen is in "pro" mode 6 | */ 7 | swapPro: boolean; 8 | 9 | /** 10 | * Set the swap screen "pro" mode 11 | * @param swapPro 12 | */ 13 | setSwapPro: (swapPro: boolean) => void; 14 | } 15 | 16 | export const useSwapPro = create((set) => ({ 17 | swapPro: false, 18 | setSwapPro: (swapPro) => set({ swapPro }), 19 | })); 20 | -------------------------------------------------------------------------------- /web/src/stores/useWelcomeStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface WelcomeStore { 4 | /** 5 | * Whether the welcome screen is visible 6 | */ 7 | welcome: boolean; 8 | 9 | /** 10 | * Set the welcome screen visibility 11 | * @param welcome 12 | */ 13 | setWelcome: (welcome: boolean) => void; 14 | 15 | /** 16 | * Whether the user is hovering over the welcome screen 17 | */ 18 | hovering: boolean; 19 | 20 | /** 21 | * Set the hovering state 22 | * @param hovering 23 | */ 24 | setHovering: (hovering: boolean) => void; 25 | } 26 | 27 | export const useWelcomeStore = create((set) => ({ 28 | welcome: true, 29 | setWelcome: (welcome) => set({ welcome }), 30 | 31 | hovering: false, 32 | setHovering: (hovering) => set({ hovering }), 33 | })); 34 | -------------------------------------------------------------------------------- /web/src/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | @use './variables' as *; 2 | 3 | @mixin box($size) { 4 | border-radius: $size; 5 | padding: calc($size * 1.5); 6 | } 7 | 8 | @mixin light { 9 | background-color: $light; 10 | color: $dark; 11 | } 12 | 13 | @mixin dark { 14 | background-color: $dark; 15 | color: $light; 16 | } 17 | -------------------------------------------------------------------------------- /web/src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | $light: #EBEBEB; 2 | $dark: #1E1E1E; 3 | -------------------------------------------------------------------------------- /web/src/styles/globals.scss: -------------------------------------------------------------------------------- 1 | body { 2 | all: unset; 3 | background-color: $light; 4 | font-size: 16px; 5 | font-weight: 400; 6 | } 7 | 8 | button { 9 | all: unset; 10 | } 11 | 12 | a { 13 | all: unset; 14 | } 15 | 16 | div[data-radix-popper-content-wrapper] { z-index: 10 !important; } 17 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": ["dom", "dom.iterable", "esnext", "es2020"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "strictNullChecks": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | }, 21 | { 22 | "name": "@0no-co/graphqlsp", 23 | "schema": "./schema.graphql" 24 | } 25 | ], 26 | "paths": { 27 | "@/*": ["./src/*"], 28 | "#/*": ["./public/*"] 29 | } 30 | }, 31 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 32 | "exclude": ["node_modules"] 33 | } 34 | --------------------------------------------------------------------------------