fix problem in wallet state mgmt
This commit is contained in:
parent
1c868995a1
commit
9b62f6ebf4
|
@ -1,19 +1,4 @@
|
||||||
import {
|
import { AccountInfo, PublicKey, Transaction } from '@solana/web3.js'
|
||||||
AccountInfo,
|
|
||||||
Connection,
|
|
||||||
PublicKey,
|
|
||||||
Transaction,
|
|
||||||
} from '@solana/web3.js'
|
|
||||||
import Wallet from '@project-serum/sol-wallet-adapter'
|
|
||||||
|
|
||||||
export interface ConnectionContextValues {
|
|
||||||
endpoint: string
|
|
||||||
setEndpoint: (newEndpoint: string) => void
|
|
||||||
connection: Connection
|
|
||||||
sendConnection: Connection
|
|
||||||
availableEndpoints: EndpointInfo[]
|
|
||||||
setCustomEndpoints: (newCustomEndpoints: EndpointInfo[]) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EndpointInfo {
|
export interface EndpointInfo {
|
||||||
name: string
|
name: string
|
||||||
|
@ -23,35 +8,12 @@ export interface EndpointInfo {
|
||||||
poolKey: string
|
poolKey: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WalletContextValues {
|
|
||||||
wallet: Wallet
|
|
||||||
connected: boolean
|
|
||||||
providerUrl: string
|
|
||||||
setProviderUrl: (newProviderUrl: string) => void
|
|
||||||
providerName: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TokenAccount {
|
export interface TokenAccount {
|
||||||
pubkey: PublicKey
|
pubkey: PublicKey
|
||||||
account: AccountInfo<Buffer> | null
|
account: AccountInfo<Buffer> | null
|
||||||
effectiveMint: PublicKey
|
effectiveMint: PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {tokenMint: preferred token account's base58 encoded public key}
|
|
||||||
*/
|
|
||||||
export interface SelectedTokenAccounts {
|
|
||||||
[tokenMint: string]: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Token infos
|
|
||||||
export interface KnownToken {
|
|
||||||
tokenSymbol: string
|
|
||||||
tokenName: string
|
|
||||||
icon?: string
|
|
||||||
mintAddress: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WalletAdapter {
|
export interface WalletAdapter {
|
||||||
publicKey: PublicKey
|
publicKey: PublicKey
|
||||||
autoApprove: boolean
|
autoApprove: boolean
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { Menu } from '@headlessui/react'
|
import { Menu } from '@headlessui/react'
|
||||||
import { LinkIcon } from '@heroicons/react/solid'
|
import { LinkIcon } from '@heroicons/react/solid'
|
||||||
import useWallet, { WALLET_PROVIDERS } from '../hooks/useWallet'
|
import { useMemo } from 'react'
|
||||||
|
import useWalletStore from '../stores/useWalletStore'
|
||||||
|
import {
|
||||||
|
getWalletProviderByUrl,
|
||||||
|
WALLET_PROVIDERS,
|
||||||
|
} from '../utils/wallet-adapters'
|
||||||
import Button from './Button'
|
import Button from './Button'
|
||||||
|
|
||||||
const ChevronDownIcon = (props) => (
|
const ChevronDownIcon = (props) => (
|
||||||
|
@ -36,7 +41,15 @@ const CheckIcon = (props) => (
|
||||||
)
|
)
|
||||||
|
|
||||||
const ConnectWalletButton = (props) => {
|
const ConnectWalletButton = (props) => {
|
||||||
const { connected, provider, setSavedProviderUrl } = useWallet()
|
const {
|
||||||
|
connected,
|
||||||
|
selectedProviderUrl,
|
||||||
|
set: setWalletStore,
|
||||||
|
} = useWalletStore((s) => s)
|
||||||
|
|
||||||
|
const provider = useMemo(() => getWalletProviderByUrl(selectedProviderUrl), [
|
||||||
|
selectedProviderUrl,
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
|
@ -65,7 +78,11 @@ const ConnectWalletButton = (props) => {
|
||||||
<Menu.Item key={name}>
|
<Menu.Item key={name}>
|
||||||
<button
|
<button
|
||||||
className="flex p-2 h-9 hover:bg-bkg-2 hover:cursor-pointer hover:rounded-2xl font-normal focus:outline-none"
|
className="flex p-2 h-9 hover:bg-bkg-2 hover:cursor-pointer hover:rounded-2xl font-normal focus:outline-none"
|
||||||
onClick={() => setSavedProviderUrl(url)}
|
onClick={() =>
|
||||||
|
setWalletStore((s) => {
|
||||||
|
s.providerUrl = url
|
||||||
|
})
|
||||||
|
}
|
||||||
style={{ width: '14rem' }}
|
style={{ width: '14rem' }}
|
||||||
>
|
>
|
||||||
<img src={icon} className="h-4 w-4 mr-2" />
|
<img src={icon} className="h-4 w-4 mr-2" />
|
||||||
|
|
|
@ -9,8 +9,8 @@ import useVaults from '../hooks/useVaults'
|
||||||
|
|
||||||
const RedeemModal = () => {
|
const RedeemModal = () => {
|
||||||
const actions = useWalletStore((s) => s.actions)
|
const actions = useWalletStore((s) => s.actions)
|
||||||
const connected = useWalletStore((s) => s.connected)
|
|
||||||
const wallet = useWalletStore((s) => s.current)
|
const wallet = useWalletStore((s) => s.current)
|
||||||
|
const connected = useWalletStore((s) => s.connected)
|
||||||
const largestAccounts = useLargestAccounts()
|
const largestAccounts = useLargestAccounts()
|
||||||
const vaults = useVaults()
|
const vaults = useVaults()
|
||||||
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
import { Menu } from '@headlessui/react'
|
|
||||||
import {
|
|
||||||
ChevronDownIcon,
|
|
||||||
ChevronUpIcon,
|
|
||||||
CheckCircleIcon,
|
|
||||||
} from '@heroicons/react/outline'
|
|
||||||
|
|
||||||
import useWalletStore from '../stores/useWalletStore'
|
|
||||||
import { WALLET_PROVIDERS, DEFAULT_PROVIDER } from '../hooks/useWallet'
|
|
||||||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
|
||||||
|
|
||||||
export default function WalletSelect({ isPrimary = false }) {
|
|
||||||
const setWalletStore = useWalletStore((s) => s.set)
|
|
||||||
const [savedProviderUrl] = useLocalStorageState(
|
|
||||||
'walletProvider',
|
|
||||||
DEFAULT_PROVIDER.url
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleSelectProvider = (url) => {
|
|
||||||
setWalletStore((state) => {
|
|
||||||
state.providerUrl = url
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Menu>
|
|
||||||
{({ open }) => (
|
|
||||||
<>
|
|
||||||
<Menu.Button
|
|
||||||
className={`flex justify-center items-center h-full rounded-r rounded-l-none focus:outline-none text-primary hover:text-fgd-1 ${
|
|
||||||
isPrimary
|
|
||||||
? 'px-3 hover:bg-primary'
|
|
||||||
: 'px-2 hover:bg-bkg-3 border-l border-fgd-4'
|
|
||||||
} cursor-pointer`}
|
|
||||||
>
|
|
||||||
{open ? (
|
|
||||||
<ChevronUpIcon className="h-5 w-5" />
|
|
||||||
) : (
|
|
||||||
<ChevronDownIcon className="h-5 w-5" />
|
|
||||||
)}
|
|
||||||
</Menu.Button>
|
|
||||||
<Menu.Items className="z-20 p-1 absolute right-0 top-11 bg-bkg-1 divide-y divide-bkg-3 shadow-lg outline-none rounded-md w-48">
|
|
||||||
{WALLET_PROVIDERS.map(({ name, url, icon }) => (
|
|
||||||
<Menu.Item key={name}>
|
|
||||||
<button
|
|
||||||
className="flex flex-row items-center justify-between w-full p-2 hover:bg-bkg-2 hover:cursor-pointer font-normal focus:outline-none"
|
|
||||||
onClick={() => handleSelectProvider(url)}
|
|
||||||
>
|
|
||||||
<div className="flex">
|
|
||||||
<img src={icon} className="w-5 h-5 mr-2" />
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
{savedProviderUrl === url ? (
|
|
||||||
<CheckCircleIcon className="h-4 w-4 text-green" />
|
|
||||||
) : null}{' '}
|
|
||||||
</button>
|
|
||||||
</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu.Items>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Menu>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,41 +1,17 @@
|
||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect, useMemo } from 'react'
|
||||||
import Wallet from '@project-serum/sol-wallet-adapter'
|
|
||||||
|
|
||||||
import { WalletAdapter } from '../@types/types'
|
import { WalletAdapter } from '../@types/types'
|
||||||
import useWalletStore from '../stores/useWalletStore'
|
import useWalletStore from '../stores/useWalletStore'
|
||||||
import { notify } from '../utils/notifications'
|
import { notify } from '../utils/notifications'
|
||||||
import {
|
import {
|
||||||
PhantomWalletAdapter,
|
DEFAULT_PROVIDER,
|
||||||
SolletExtensionAdapter,
|
getWalletProviderByUrl,
|
||||||
} from '../utils/wallet-adapters'
|
} from '../utils/wallet-adapters'
|
||||||
|
|
||||||
import useInterval from './useInterval'
|
import useInterval from './useInterval'
|
||||||
import useLocalStorageState from './useLocalStorageState'
|
import useLocalStorageState from './useLocalStorageState'
|
||||||
|
|
||||||
const SECONDS = 1000
|
const SECONDS = 1000
|
||||||
const ASSET_URL =
|
|
||||||
'https://cdn.jsdelivr.net/gh/solana-labs/oyster@main/assets/wallets'
|
|
||||||
|
|
||||||
export const WALLET_PROVIDERS = [
|
|
||||||
{
|
|
||||||
name: 'Sollet.io',
|
|
||||||
url: 'https://www.sollet.io',
|
|
||||||
icon: `${ASSET_URL}/sollet.svg`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Sollet Extension',
|
|
||||||
url: 'https://www.sollet.io/extension',
|
|
||||||
icon: `${ASSET_URL}/sollet.svg`,
|
|
||||||
adapter: SolletExtensionAdapter as any,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Phantom',
|
|
||||||
url: 'https://www.phantom.app',
|
|
||||||
icon: `https://www.phantom.app/img/logo.png`,
|
|
||||||
adapter: PhantomWalletAdapter,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const DEFAULT_PROVIDER = WALLET_PROVIDERS[0]
|
|
||||||
|
|
||||||
export default function useWallet() {
|
export default function useWallet() {
|
||||||
const {
|
const {
|
||||||
|
@ -50,13 +26,12 @@ export default function useWallet() {
|
||||||
'walletProvider',
|
'walletProvider',
|
||||||
DEFAULT_PROVIDER.url
|
DEFAULT_PROVIDER.url
|
||||||
)
|
)
|
||||||
const provider = useMemo(
|
const provider = useMemo(() => getWalletProviderByUrl(selectedProviderUrl), [
|
||||||
() => WALLET_PROVIDERS.find(({ url }) => url === savedProviderUrl),
|
selectedProviderUrl,
|
||||||
[savedProviderUrl]
|
])
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedProviderUrl) {
|
if (selectedProviderUrl && selectedProviderUrl != savedProviderUrl) {
|
||||||
setSavedProviderUrl(selectedProviderUrl)
|
setSavedProviderUrl(selectedProviderUrl)
|
||||||
}
|
}
|
||||||
}, [selectedProviderUrl])
|
}, [selectedProviderUrl])
|
||||||
|
@ -65,8 +40,8 @@ export default function useWallet() {
|
||||||
if (provider) {
|
if (provider) {
|
||||||
const updateWallet = () => {
|
const updateWallet = () => {
|
||||||
// hack to also update wallet synchronously in case it disconnects
|
// hack to also update wallet synchronously in case it disconnects
|
||||||
const wallet = new (provider.adapter || Wallet)(
|
const wallet = new provider.adapter(
|
||||||
savedProviderUrl,
|
provider.url,
|
||||||
endpoint
|
endpoint
|
||||||
) as WalletAdapter
|
) as WalletAdapter
|
||||||
setWalletStore((state) => {
|
setWalletStore((state) => {
|
||||||
|
@ -86,7 +61,7 @@ export default function useWallet() {
|
||||||
updateWallet()
|
updateWallet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [provider, savedProviderUrl, endpoint])
|
}, [provider, endpoint])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!wallet) return
|
if (!wallet) return
|
||||||
|
@ -116,14 +91,14 @@ export default function useWallet() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return () => {
|
return () => {
|
||||||
if (wallet && wallet.connected) {
|
if (wallet) {
|
||||||
wallet.disconnect()
|
wallet.disconnect()
|
||||||
}
|
}
|
||||||
setWalletStore((state) => {
|
setWalletStore((state) => {
|
||||||
state.connected = false
|
state.connected = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [wallet, setWalletStore])
|
}, [wallet])
|
||||||
|
|
||||||
// fetch pool on page load
|
// fetch pool on page load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -139,5 +114,5 @@ export default function useWallet() {
|
||||||
actions.fetchUsdcVault()
|
actions.fetchUsdcVault()
|
||||||
}, 20 * SECONDS)
|
}, 20 * SECONDS)
|
||||||
|
|
||||||
return { connected, wallet, provider, setSavedProviderUrl }
|
return { connected, wallet }
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { findLargestBalanceAccountForMint } from '../hooks/useLargestAccounts'
|
||||||
import { TOKEN_PROGRAM_ID } from '@solana/spl-token'
|
import { TOKEN_PROGRAM_ID } from '@solana/spl-token'
|
||||||
import { createAssociatedTokenAccount } from '../utils/associated'
|
import { createAssociatedTokenAccount } from '../utils/associated'
|
||||||
import { sendTransaction } from '../utils/send'
|
import { sendTransaction } from '../utils/send'
|
||||||
|
import { DEFAULT_PROVIDER } from '../utils/wallet-adapters'
|
||||||
|
|
||||||
export const ENDPOINTS: EndpointInfo[] = [
|
export const ENDPOINTS: EndpointInfo[] = [
|
||||||
{
|
{
|
||||||
|
@ -97,7 +98,7 @@ const useWalletStore = create<WalletStore>((set, get) => ({
|
||||||
programId: PROGRAM_ID,
|
programId: PROGRAM_ID,
|
||||||
},
|
},
|
||||||
current: null,
|
current: null,
|
||||||
providerUrl: null,
|
providerUrl: DEFAULT_PROVIDER.url,
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
program: undefined,
|
program: undefined,
|
||||||
pool: undefined,
|
pool: undefined,
|
||||||
|
@ -112,7 +113,7 @@ const useWalletStore = create<WalletStore>((set, get) => ({
|
||||||
const programId = get().connection.programId
|
const programId = get().connection.programId
|
||||||
const set = get().set
|
const set = get().set
|
||||||
|
|
||||||
console.log('fetchPool', connection, poolIdl)
|
// console.log('fetchPool', connection, poolIdl)
|
||||||
if (connection) {
|
if (connection) {
|
||||||
const provider = new anchor.Provider(
|
const provider = new anchor.Provider(
|
||||||
connection,
|
connection,
|
||||||
|
@ -129,7 +130,7 @@ const useWalletStore = create<WalletStore>((set, get) => ({
|
||||||
getTokenAccount(connection, pool.poolWatermelon),
|
getTokenAccount(connection, pool.poolWatermelon),
|
||||||
])
|
])
|
||||||
|
|
||||||
console.log({ program, pool, usdcVault, mangoVault })
|
// console.log('fetchPool', { program, pool, usdcVault, mangoVault })
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.provider = provider
|
state.provider = provider
|
||||||
|
@ -147,6 +148,12 @@ const useWalletStore = create<WalletStore>((set, get) => ({
|
||||||
const walletOwner = wallet?.publicKey
|
const walletOwner = wallet?.publicKey
|
||||||
const set = get().set
|
const set = get().set
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'fetchWalletTokenAccounts',
|
||||||
|
connected,
|
||||||
|
walletOwner?.toString()
|
||||||
|
)
|
||||||
|
|
||||||
if (connected && walletOwner) {
|
if (connected && walletOwner) {
|
||||||
const ownedTokenAccounts = await getOwnedTokenAccounts(
|
const ownedTokenAccounts = await getOwnedTokenAccounts(
|
||||||
connection,
|
connection,
|
||||||
|
@ -190,12 +197,12 @@ const useWalletStore = create<WalletStore>((set, get) => ({
|
||||||
const mints = await Promise.all(
|
const mints = await Promise.all(
|
||||||
mintKeys.map((pk) => getMint(connection, pk))
|
mintKeys.map((pk) => getMint(connection, pk))
|
||||||
)
|
)
|
||||||
console.log('fetchMints', mints)
|
// console.log('fetchMints', mints)
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
for (const pa of mints) {
|
for (const pa of mints) {
|
||||||
state.mints[pa.publicKey.toBase58()] = pa.account
|
state.mints[pa.publicKey.toBase58()] = pa.account
|
||||||
console.log('mint', pa.publicKey.toBase58(), pa.account)
|
// console.log('mint', pa.publicKey.toBase58(), pa.account)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -293,8 +300,6 @@ const useWalletStore = create<WalletStore>((set, get) => ({
|
||||||
actions.fetchUsdcVault()
|
actions.fetchUsdcVault()
|
||||||
},
|
},
|
||||||
async redeem() {
|
async redeem() {
|
||||||
console.log('redeem')
|
|
||||||
|
|
||||||
const actions = get().actions
|
const actions = get().actions
|
||||||
await actions.fetchWalletTokenAccounts()
|
await actions.fetchWalletTokenAccounts()
|
||||||
|
|
||||||
|
@ -306,6 +311,7 @@ const useWalletStore = create<WalletStore>((set, get) => ({
|
||||||
current: wallet,
|
current: wallet,
|
||||||
connection: { current: connection },
|
connection: { current: connection },
|
||||||
} = get()
|
} = get()
|
||||||
|
|
||||||
const redeemable = findLargestBalanceAccountForMint(
|
const redeemable = findLargestBalanceAccountForMint(
|
||||||
mints,
|
mints,
|
||||||
tokenAccounts,
|
tokenAccounts,
|
||||||
|
@ -317,7 +323,7 @@ const useWalletStore = create<WalletStore>((set, get) => ({
|
||||||
pool.watermelonMint
|
pool.watermelonMint
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log(redeemable, watermelon, 'exchangeRedeemableForMango')
|
console.log('exchangeRedeemableForMango', redeemable, watermelon)
|
||||||
|
|
||||||
const [poolSigner] = await anchor.web3.PublicKey.findProgramAddress(
|
const [poolSigner] = await anchor.web3.PublicKey.findProgramAddress(
|
||||||
[pool.watermelonMint.toBuffer()],
|
[pool.watermelonMint.toBuffer()],
|
||||||
|
|
|
@ -1,2 +1,32 @@
|
||||||
export * from './phantom'
|
import Wallet from '@project-serum/sol-wallet-adapter'
|
||||||
export * from './sollet-extension'
|
import { PhantomWalletAdapter } from './phantom'
|
||||||
|
import { SolletExtensionAdapter } from './sollet-extension'
|
||||||
|
|
||||||
|
const ASSET_URL =
|
||||||
|
'https://cdn.jsdelivr.net/gh/solana-labs/oyster@main/assets/wallets'
|
||||||
|
|
||||||
|
export const WALLET_PROVIDERS = [
|
||||||
|
{
|
||||||
|
name: 'Sollet.io',
|
||||||
|
url: 'https://www.sollet.io',
|
||||||
|
icon: `${ASSET_URL}/sollet.svg`,
|
||||||
|
adapter: Wallet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Sollet Extension',
|
||||||
|
url: 'https://www.sollet.io/extension',
|
||||||
|
icon: `${ASSET_URL}/sollet.svg`,
|
||||||
|
adapter: SolletExtensionAdapter as any,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Phantom',
|
||||||
|
url: 'https://www.phantom.app',
|
||||||
|
icon: `https://www.phantom.app/img/logo.png`,
|
||||||
|
adapter: PhantomWalletAdapter,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const DEFAULT_PROVIDER = WALLET_PROVIDERS[0]
|
||||||
|
|
||||||
|
export const getWalletProviderByUrl = (urlOrNull) =>
|
||||||
|
WALLET_PROVIDERS.find(({ url }) => url === urlOrNull) || DEFAULT_PROVIDER
|
||||||
|
|
Loading…
Reference in New Issue