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"