diff --git a/package.json b/package.json index 19ceb2410..63f88a342 100755 --- a/package.json +++ b/package.json @@ -210,6 +210,7 @@ "@gooddollar/goodprotocol": "2.2.1", "@gooddollar/web3sdk": "0.1.58", "@gooddollar/web3sdk-v2": "^0.4.37", + "@goodsdks/savings-widget": "^1.0.0", "@headlessui/react": "1.5.0", "@lingui/format-json": "^4.0.0", "@lingui/macro": "^4.0.0", diff --git a/src/components/Faq/Faq.tsx b/src/components/Faq/Faq.tsx index 20e2f3506..65b2a1916 100644 --- a/src/components/Faq/Faq.tsx +++ b/src/components/Faq/Faq.tsx @@ -9,7 +9,7 @@ import { AdditionalChainId } from 'constants/index' import { faqs, faqReserveCeloCopy, faqReserveXdcCopy } from './copies' -export type FaqType = 'swap' | 'buy' | 'bridge' | 'goodid' | 'claim' | 'reserve' +export type FaqType = 'swap' | 'buy' | 'bridge' | 'goodid' | 'claim' | 'reserve' | 'savings' const FaqItem = ({ id, question, answer, links, AltLink }) => { const { isDesktopView } = useScreenSize() diff --git a/src/components/Faq/copies.tsx b/src/components/Faq/copies.tsx index bd0d1d41c..2daea6b42 100644 --- a/src/components/Faq/copies.tsx +++ b/src/components/Faq/copies.tsx @@ -290,6 +290,33 @@ const faqClaimCopy: FaqItemCopy[] = [ }, ] +const faqSavingsCopy: FaqItemCopy[] = [ + { + id: 'whatissavings', + question: `What is the Savings Widget?`, + answer: `The Savings Widget allows you to grow your G$ holdings through yield-generating opportunities. Connect your wallet and earn rewards on your GoodDollar holdings securely and transparently.`, + links: [], + }, + { + id: 'howdoesitsavings', + question: `How does it work?`, + answer: `Once connected, your G$ can be staked or used in various yield-generating strategies. The widget displays your balance, potential earnings, and available actions. All transactions are transparent and happen on-chain.`, + links: [], + }, + { + id: 'issavingssafe', + question: `Is it safe?`, + answer: `Your funds remain in your control. The smart contracts powering the savings strategies have been audited and follow security best practices. Always verify contract addresses before interacting.`, + links: [], + }, + { + id: 'whataresavingsrewards', + question: `What are the rewards?`, + answer: `Rewards vary based on the yield-generating strategy and current market conditions. Check the widget for current rates and estimated earnings. Rewards are typically paid in G$ or other supported tokens.`, + links: [], + }, +] + export const faqReserveCeloCopy: FaqItemCopy[] = [ { id: 'whatsswapreserve', @@ -401,4 +428,5 @@ export const faqs = { goodid: faqGoodIDCopy, claim: faqClaimCopy, reserve: faqReserveCeloCopy, + savings: faqSavingsCopy, } diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 9cd102cb1..1e10020e9 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -256,6 +256,11 @@ export default function SideBar({ mobile, closeSidebar }: { mobile?: boolean; cl text: 'Stake', show: !isMinipay, }, + { + route: '/savings', + text: 'Savings', + show: !networkEnv.includes('production') ?? !isMinipay, + }, { route: '/portfolio', text: 'Portfolio', diff --git a/src/pages/App.tsx b/src/pages/App.tsx index d6de23914..2ec478936 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -17,10 +17,8 @@ import { updateUserDarkMode } from '../state/user/actions' import SideBar from '../components/SideBar' import TransactionUpdater from '../state/transactions/updater' import useSendAnalyticsData from 'hooks/useSendAnalyticsData' -import WalletChat from '../components/WalletChat' import { useIsSimpleApp } from 'state/simpleapp/simpleapp' import MainPageContainer from 'components/Layout/MainPageContainer' -import { useFeatureFlag } from 'posthog-react-native' import { isMiniPay } from 'utils/minipay' export const Beta = styled.div` @@ -101,7 +99,7 @@ function App(): JSX.Element { const isMinipay = isMiniPay() const { open, url, onClose } = useRedirectNotice() const { isDesktopView } = useScreenSize() - const walletChatEnabled = useFeatureFlag('wallet-chat') + // const walletChatEnabled = useFeatureFlag('wallet-chat') void useFaucet() @@ -190,7 +188,7 @@ function App(): JSX.Element { - {!isSimpleApp && !sidebarOpen && walletChatEnabled && } + {/* {!isSimpleApp && !sidebarOpen && walletChatEnabled && } */} ) diff --git a/src/pages/gd/Savings/index.tsx b/src/pages/gd/Savings/index.tsx new file mode 100644 index 000000000..d6cc17f1b --- /dev/null +++ b/src/pages/gd/Savings/index.tsx @@ -0,0 +1,41 @@ +import '@goodsdks/savings-widget' +import { useEffect, useRef, useCallback } from 'react' +import { useAppKit, useAppKitAccount, useAppKitProvider } from '@reown/appkit/react' +import type { Provider } from '@reown/appkit/react' +import { PageLayout } from 'components/Layout/PageLayout' + +export default function Savings() { + const widgetRef = useRef< + HTMLElement & { + connectWallet?: () => void + web3Provider?: unknown | null + } + >(null) + const { open: openAppKit } = useAppKit() + const { address } = useAppKitAccount() + const { walletProvider } = useAppKitProvider('eip155') + + const handleConnectWallet = useCallback(async () => { + await openAppKit({ view: 'Connect' }) + }, [openAppKit]) + + // Assign connect wallet handler + useEffect(() => { + const widget = widgetRef.current + if (!widget) return + widget.connectWallet = handleConnectWallet + }, [handleConnectWallet]) + + // Update web3 provider based on wallet connection state + useEffect(() => { + const widget = widgetRef.current + if (!widget) return + widget.web3Provider = address && walletProvider ? walletProvider : null + }, [address, walletProvider]) + + return ( + + + + ) +} diff --git a/src/routes.tsx b/src/routes.tsx index 4eb465c42..45020ff2e 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -15,6 +15,7 @@ const Portfolio = lazy(() => import('./pages/gd/Portfolio')) const MicroBridge = lazy(() => import('./pages/gd/MicroBridge')) const GoodBridge = lazy(() => import('./pages/gd/GoodBridge')) const Claim = lazy(() => import('./pages/gd/Claim/')) +const Savings = lazy(() => import('./pages/gd/Savings')) const GoodId = lazy(() => import('./pages/gd/GoodId')) const BuyGd = lazy(() => import('./pages/gd/BuyGD')) const NewsFeedPage = lazy(() => import('./pages/gd/News')) @@ -55,6 +56,7 @@ function Routes(): JSX.Element { + diff --git a/tsconfig.json b/tsconfig.json index dec69263f..9a4d01c28 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,6 @@ "baseUrl": "./src", "typeRoots": ["./node_modules/@types", "./types", "./node_modules/@react-spring/types", "./src"] }, - "include": ["src"], + "include": ["src", "types"], "exclude": ["node_modules"] } diff --git a/types/gooddollar-savings-widget.d.ts b/types/gooddollar-savings-widget.d.ts new file mode 100644 index 000000000..87c3a15dc --- /dev/null +++ b/types/gooddollar-savings-widget.d.ts @@ -0,0 +1,19 @@ +import 'react' + +declare module 'react' { + namespace JSX { + interface IntrinsicElements { + 'gooddollar-savings-widget': React.DetailedHTMLProps< + React.HTMLAttributes, + HTMLElement + > & { + ref?: React.Ref< + HTMLElement & { + connectWallet?: () => void + web3Provider?: unknown | null + } + > + } + } + } +} diff --git a/yarn.lock b/yarn.lock index cef542b6a..8a3f533a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26,6 +26,13 @@ __metadata: languageName: node linkType: hard +"@adraffy/ens-normalize@npm:^1.11.0": + version: 1.11.1 + resolution: "@adraffy/ens-normalize@npm:1.11.1" + checksum: e8b17fcc730ccc45a956e1fbb09edfe42be41c291079512082e9964f8ef4287e67913183cdd02fff71d2e215340d5b98a9bbbd9be32c5d36fad4ba2c1ec33ff2 + languageName: node + linkType: hard + "@alloc/quick-lru@npm:^5.2.0": version: 5.2.0 resolution: "@alloc/quick-lru@npm:5.2.0" @@ -4866,6 +4873,7 @@ __metadata: "@gooddollar/goodprotocol": 2.2.1 "@gooddollar/web3sdk": 0.1.58 "@gooddollar/web3sdk-v2": ^0.4.37 + "@goodsdks/savings-widget": ^1.0.0 "@headlessui/react": 1.5.0 "@hot-loader/react-dom": ^17.0.1 "@lingui/cli": ^4.0.0 @@ -5130,6 +5138,16 @@ __metadata: languageName: node linkType: hard +"@goodsdks/savings-widget@npm:^1.0.0": + version: 1.0.0 + resolution: "@goodsdks/savings-widget@npm:1.0.0" + dependencies: + lit: ^3.1.0 + viem: latest + checksum: a8f89bc9f836f5e5d5f485fa960ff9e48066c68dfaf284612b8d9c985aa79a491ca776fae09a568612cd6b4ac5264ddf1a019dcbc5f2a3b98b7ac5972c526ec6 + languageName: node + linkType: hard + "@graphql-typed-document-node/core@npm:^3.1.1, @graphql-typed-document-node/core@npm:^3.2.0": version: 3.2.0 resolution: "@graphql-typed-document-node/core@npm:3.2.0" @@ -5804,6 +5822,13 @@ __metadata: languageName: node linkType: hard +"@lit-labs/ssr-dom-shim@npm:^1.5.0": + version: 1.5.1 + resolution: "@lit-labs/ssr-dom-shim@npm:1.5.1" + checksum: 22c404c4813bceccaba2b6ee533251b58a2f4e9669507fcbd021120c66fbc2ae1daea4d1e31a76e450fa6a16942471579c4d1e256aa65ec1172d5aac0c0197e1 + languageName: node + linkType: hard + "@lit/reactive-element@npm:^1.3.0, @lit/reactive-element@npm:^1.6.0": version: 1.6.2 resolution: "@lit/reactive-element@npm:1.6.2" @@ -5822,6 +5847,15 @@ __metadata: languageName: node linkType: hard +"@lit/reactive-element@npm:^2.1.0": + version: 2.1.2 + resolution: "@lit/reactive-element@npm:2.1.2" + dependencies: + "@lit-labs/ssr-dom-shim": ^1.5.0 + checksum: e0ed931b9cd50ce9521db7da528c00e1a8b912a66ac7b0f43554992e43cc7c20735b34bf99750d86b9256228818835ca2e9bf517653b246c73a6aac92cdd894c + languageName: node + linkType: hard + "@magiklabs/react-sdk@npm:^1.0.8": version: 1.0.9 resolution: "@magiklabs/react-sdk@npm:1.0.9" @@ -6300,6 +6334,13 @@ __metadata: languageName: node linkType: hard +"@noble/ciphers@npm:^1.3.0": + version: 1.3.0 + resolution: "@noble/ciphers@npm:1.3.0" + checksum: 19722c35475df9bc78db60d261d0b5ef8a6d722561efc2135453f943eaa421b492195dc666e3e4df2b755bca3739e04f04b9c660198559f5dd05d3cfbf1b9e92 + languageName: node + linkType: hard + "@noble/curves@npm:1.0.0, @noble/curves@npm:~1.0.0": version: 1.0.0 resolution: "@noble/curves@npm:1.0.0" @@ -6345,6 +6386,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:1.9.1": + version: 1.9.1 + resolution: "@noble/curves@npm:1.9.1" + dependencies: + "@noble/hashes": 1.8.0 + checksum: 4f3483a1001538d2f55516cdcb19319d1eaef79550633f670e7d570b989cdbc0129952868b72bb67643329746b8ffefe8e4cd791c8cc35574e05a37f873eef42 + languageName: node + linkType: hard + "@noble/curves@npm:^1.4.0, @noble/curves@npm:^1.6.0, @noble/curves@npm:~1.7.0": version: 1.7.0 resolution: "@noble/curves@npm:1.7.0" @@ -6354,6 +6404,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:~1.9.0": + version: 1.9.7 + resolution: "@noble/curves@npm:1.9.7" + dependencies: + "@noble/hashes": 1.8.0 + checksum: 65acad44ac6944ab96471109087d6cfcbcaa251faad6295961be9a5ace220634f4b7c74a96d1ee2274ad3880ea953d8e8259893ed8c906c831ef29f5c04ec9cc + languageName: node + linkType: hard + "@noble/ed25519@npm:^1.7.3": version: 1.7.3 resolution: "@noble/ed25519@npm:1.7.3" @@ -6403,6 +6462,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1.8.0, @noble/hashes@npm:~1.8.0": + version: 1.8.0 + resolution: "@noble/hashes@npm:1.8.0" + checksum: c94e98b941963676feaba62475b1ccfa8341e3f572adbb3b684ee38b658df44100187fa0ef4220da580b13f8d27e87d5492623c8a02ecc61f23fb9960c7918f5 + languageName: node + linkType: hard + "@noble/hashes@npm:^1.3.2, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.5.0, @noble/hashes@npm:~1.6.0": version: 1.6.1 resolution: "@noble/hashes@npm:1.6.1" @@ -8693,6 +8759,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.2.5": + version: 1.2.6 + resolution: "@scure/base@npm:1.2.6" + checksum: 1058cb26d5e4c1c46c9cc0ae0b67cc66d306733baf35d6ebdd8ddaba242b80c3807b726e3b48cb0411bb95ec10d37764969063ea62188f86ae9315df8ea6b325 + languageName: node + linkType: hard + "@scure/bip32@npm:1.3.0": version: 1.3.0 resolution: "@scure/bip32@npm:1.3.0" @@ -8737,6 +8810,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.7.0, @scure/bip32@npm:^1.7.0": + version: 1.7.0 + resolution: "@scure/bip32@npm:1.7.0" + dependencies: + "@noble/curves": ~1.9.0 + "@noble/hashes": ~1.8.0 + "@scure/base": ~1.2.5 + checksum: c83adca5a74ec5c4ded8ba93900d0065e4767c4759cf24c2674923aef01d45ba56f171574e3519f2341be99f53a333f01b674eb6cfeb6fa8379607c6d1bc90b5 + languageName: node + linkType: hard + "@scure/bip32@npm:^1.5.0": version: 1.6.0 resolution: "@scure/bip32@npm:1.6.0" @@ -8788,6 +8872,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.6.0, @scure/bip39@npm:^1.6.0": + version: 1.6.0 + resolution: "@scure/bip39@npm:1.6.0" + dependencies: + "@noble/hashes": ~1.8.0 + "@scure/base": ~1.2.5 + checksum: 96d46420780473d6c6c9700254a0eceec60302f61d7f9d7f29024e90c7acff3e8e40a5ee52dfaf104db539a10462e531996aaf9e69f082b8540b0a25870545fc + languageName: node + linkType: hard + "@scure/bip39@npm:^1.2.1, @scure/bip39@npm:^1.4.0": version: 1.5.0 resolution: "@scure/bip39@npm:1.5.0" @@ -14319,6 +14413,36 @@ __metadata: languageName: node linkType: hard +"abitype@npm:1.2.3": + version: 1.2.3 + resolution: "abitype@npm:1.2.3" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: b5b5620f8e55a6dd7ae829630c0ded02b30f589f0f8f5ca931cdfcf6d7daa8154e30e3fe3593b3f6c4872a955ac55d447ccc2f801fd6a6aa698bdad966e3fe2e + languageName: node + linkType: hard + +"abitype@npm:^1.2.3": + version: 1.2.4 + resolution: "abitype@npm:1.2.4" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: 8f490702d38db1fd18513ac2a2c06e1e854008f9810f32cff488ef4dc78132444cbdede8611ed75f4f100c04788392e4deac36d7ee3547c0f8470d2d668d187d + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -23435,6 +23559,15 @@ __metadata: languageName: node linkType: hard +"isows@npm:1.0.7": + version: 1.0.7 + resolution: "isows@npm:1.0.7" + peerDependencies: + ws: "*" + checksum: 044b949b369872882af07b60b613b5801ae01b01a23b5b72b78af80c8103bbeed38352c3e8ceff13a7834bc91fd2eb41cf91ec01d59a041d8705680e6b0ec546 + languageName: node + linkType: hard + "isstream@npm:~0.1.2": version: 0.1.2 resolution: "isstream@npm:0.1.2" @@ -24483,6 +24616,17 @@ __metadata: languageName: node linkType: hard +"lit-element@npm:^4.2.0": + version: 4.2.2 + resolution: "lit-element@npm:4.2.2" + dependencies: + "@lit-labs/ssr-dom-shim": ^1.5.0 + "@lit/reactive-element": ^2.1.0 + lit-html: ^3.3.0 + checksum: 554254b87c7a5b486351a4aecb78919f3665d91de0466c85041572e37f94dd4887f7a4765a90a59e3933f1a54fcf68c20a411319bf06c3e8dce6cfac8de4ca30 + languageName: node + linkType: hard + "lit-html@npm:^2.7.0, lit-html@npm:^2.8.0": version: 2.8.0 resolution: "lit-html@npm:2.8.0" @@ -24501,6 +24645,15 @@ __metadata: languageName: node linkType: hard +"lit-html@npm:^3.3.0": + version: 3.3.2 + resolution: "lit-html@npm:3.3.2" + dependencies: + "@types/trusted-types": ^2.0.2 + checksum: 7fe23502ca77e1f29a421507ee4f104a53226d540a11d0f1b16fa4868c962c52b9595064d28bfbc4bd910adf50b3b01d63f54ddb19a1342d572d1adf1b2ffae2 + languageName: node + linkType: hard + "lit@npm:2.8.0": version: 2.8.0 resolution: "lit@npm:2.8.0" @@ -24523,6 +24676,17 @@ __metadata: languageName: node linkType: hard +"lit@npm:^3.1.0": + version: 3.3.2 + resolution: "lit@npm:3.3.2" + dependencies: + "@lit/reactive-element": ^2.1.0 + lit-element: ^4.2.0 + lit-html: ^3.3.0 + checksum: 2136d4beac0b8be821d9ce3c4a81c9300051e1d10031f8fbe5f690faecd02efaed70e0a12eaa2694599e7fbf8888693c1d190cc0e5fe2a1d96bcbf5b01d6a042 + languageName: node + linkType: hard + "load-bmfont@npm:^1.3.1": version: 1.4.1 resolution: "load-bmfont@npm:1.4.1" @@ -27572,6 +27736,27 @@ __metadata: languageName: node linkType: hard +"ox@npm:0.14.20": + version: 0.14.20 + resolution: "ox@npm:0.14.20" + dependencies: + "@adraffy/ens-normalize": ^1.11.0 + "@noble/ciphers": ^1.3.0 + "@noble/curves": 1.9.1 + "@noble/hashes": ^1.8.0 + "@scure/bip32": ^1.7.0 + "@scure/bip39": ^1.6.0 + abitype: ^1.2.3 + eventemitter3: 5.0.1 + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 75adc672d5f61918abda1d4b9ac8278a99c50f8684c858d88e8051202d9af5331edfce903ee53a24cfef463af6b3fd2202fc7b5b018d1bf088e1e67e71e7e8ed + languageName: node + linkType: hard + "ox@npm:0.6.7": version: 0.6.7 resolution: "ox@npm:0.6.7" @@ -35221,6 +35406,27 @@ __metadata: languageName: node linkType: hard +"viem@npm:latest": + version: 2.48.6 + resolution: "viem@npm:2.48.6" + dependencies: + "@noble/curves": 1.9.1 + "@noble/hashes": 1.8.0 + "@scure/bip32": 1.7.0 + "@scure/bip39": 1.6.0 + abitype: 1.2.3 + isows: 1.0.7 + ox: 0.14.20 + ws: 8.18.3 + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 7bd8b47f9547f216d6ed452720c894d3e3c6b67643f2365e8fb0c78217d6171ca8ddc5520f1b151821fa40b2aa2dd642047949eb29fbd76e552073e13fbe5b1a + languageName: node + linkType: hard + "vite-plugin-dynamic-import@npm:^1.3.4": version: 1.5.0 resolution: "vite-plugin-dynamic-import@npm:1.5.0" @@ -36296,6 +36502,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.18.3": + version: 8.18.3 + resolution: "ws@npm:8.18.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: d64ef1631227bd0c5fe21b3eb3646c9c91229402fb963d12d87b49af0a1ef757277083af23a5f85742bae1e520feddfb434cb882ea59249b15673c16dc3f36e0 + languageName: node + linkType: hard + "ws@npm:^3.0.0": version: 3.3.3 resolution: "ws@npm:3.3.3"