This commit is contained in:
Linkie Link 2023-02-03 20:30:14 +01:00
parent 5f019f3fdd
commit 04537f8ad8
No known key found for this signature in database
GPG Key ID: 5318B0F2564D38EA
114 changed files with 5189 additions and 8040 deletions

View File

@ -2,11 +2,11 @@
![mars-banner-1200w](https://marsprotocol.io/banner.png)
## Web App
## 1. Web App
This project is a [NextJS](https://nextjs.org/). React application.
The project utilises [React hooks](https://reactjs.org/docs/hooks-intro.html), functional components, Zustand for state management, and useQuery for general data fetching and management.
The project utilises [React hooks](https://reactjs.org/docs/hooks-intro.html), functional components, [Zustand](https://github.com/pmndrs/zustand) for state management, and [useQuery](https://github.com/TanStack/query) for general data fetching and management.
Typescript is added and utilised (but optional if you want to create .jsx or .tsx files).
@ -14,7 +14,7 @@ SCSS with [CSS modules](https://create-react-app.dev/docs/adding-a-css-modules-s
Sentry is used for front end error logging/exception & bug reporting.
## Deployment
## 2. Deployment
Start web server
@ -22,16 +22,18 @@ Start web server
yarn && yarn dev
```
### Contributing
## 3. Text and translations
We welcome and encourage contributions! Please create a pull request with as much information about the work you did and what your motivation/intention was.
This repository makes use of a [translation repository](https://github.com/mars-protocol/translations). This repository containes all of the translation key values that are used within the UI. The rationale is to have no _hardcoded_ display string values in this repository.
## Imports
## 4. Development practices
### 4.1 Imports
Local components are imported via index files, which can be automatically generated with `yarn index`. This command targets index.ts files with a specific pattern in order to automate component exports. This results in clean imports throughout the pages:
```
import { Button, Card, Titlte } from 'components/common'
import { Button, Card, Title } from 'components/common'
```
or
@ -42,6 +44,16 @@ import { Breakdown, RepayInput } from 'components/fields'
In order for this to work, components are place in a folder with UpperCamelCase with the respective Component.tsx file. The component cannot be exported at default, so rather export the `const` instead.
## License
### 4.2 Data orchestration
Data is handled with a combination of container components, useQuery and Zustand. Container components are responsible for syncing the application state with the wallet-provider state. This fire of the required queries in useQuery, which are for many cases also stored in Zustand.
We aim to have as much as possible available in Zustand, to have one source of truth.
## 5. Contributing
We welcome and encourage contributions! Please create a pull request with as much information about the work you did and what your motivation/intention was.
## 6. License
Contents of this repository are open source under the [Mars Protocol Web Application License Agreement](./LICENSE).

View File

@ -1,7 +1,7 @@
{
"name": "mars",
"homepage": "./",
"version": "1.0.0",
"version": "1.1.0",
"private": false,
"license": "SEE LICENSE IN LICENSE FILE",
"scripts": {
@ -22,48 +22,48 @@
"@cosmjs/launchpad": "^0.27.1",
"@cosmjs/proto-signing": "^0.29.5",
"@cosmjs/stargate": "^0.29.5",
"@marsprotocol/wallet-connector": "^0.9.12",
"@marsprotocol/wallet-connector": "^1.2.3",
"@material-ui/core": "^4.12.4",
"@material-ui/icons": "^4.11.3",
"@ramonak/react-progress-bar": "^5.0.2",
"@sentry/nextjs": "^7.12.1",
"@tanstack/react-query": "^4.3.4",
"@tanstack/react-table": "^8.5.13",
"@testing-library/dom": "^8.17.1",
"@ramonak/react-progress-bar": "^5.0.3",
"@sentry/nextjs": "^7.34.0",
"@tanstack/react-query": "^4.24.4",
"@tanstack/react-table": "^8.7.9",
"@testing-library/dom": "^8.20.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"@tippyjs/react": "^4.2.6",
"bignumber.js": "^9.1.0",
"chart.js": "^3.9.1",
"classnames": "^2.3.1",
"bignumber.js": "^9.1.1",
"chart.js": "^4.2.0",
"classnames": "^2.3.2",
"create-conical-gradient": "^1.1.0",
"graphql": "^16.6.0",
"graphql-request": "^5.0.0",
"i18next": "^21.9.1",
"i18next-browser-languagedetector": "^6.1.5",
"i18next-http-backend": "^1.4.1",
"graphql-request": "^5.1.0",
"i18next": "^22.4.9",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-http-backend": "^2.1.1",
"lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1",
"moment": "^2.29.4",
"moment-duration-format": "^2.3.2",
"next": "^12.2.5",
"next": "^13.1.6",
"numeral": "^2.0.6",
"ramda": "^0.28.0",
"react": "^18.2.0",
"react-chartjs-2": "^4.3.1",
"react-currency-input-field": "^3.6.4",
"react-chartjs-2": "^5.2.0",
"react-currency-input-field": "^3.6.9",
"react-device-detect": "^2.2.2",
"react-dom": "^18.2.0",
"react-i18next": "^11.18.5",
"react-spring": "^9.5.5",
"react-i18next": "^12.1.4",
"react-spring": "^9.6.1",
"react-table": "^7.8.0",
"react-use-clipboard": "^1.0.8",
"sass": "^1.56.1",
"typescript": "^4.8.2",
"web-vitals": "^3.0.1",
"zustand": "^4.1.1"
"react-use-clipboard": "^1.0.9",
"sass": "^1.57.1",
"typescript": "^4.9.5",
"web-vitals": "^3.1.1",
"zustand": "^4.3.2"
},
"browserslist": {
"production": [
@ -80,26 +80,26 @@
"devDependencies": {
"@types/chart.js": "^2.9.37",
"@types/classnames": "^2.3.1",
"@types/jest": "^29.2.3",
"@types/jest": "^29.4.0",
"@types/lodash.clonedeep": "^4.5.7",
"@types/lodash.isequal": "^4.5.6",
"@types/lodash.throttle": "^4.1.7",
"@types/node": "^18.7.15",
"@types/node": "^18.11.18",
"@types/numeral": "^2.0.2",
"@types/prettier": "^2.7.0",
"@types/ramda": "^0.28.15",
"@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6",
"@types/react-table": "^7.7.12",
"eslint": "^8.23.0",
"eslint-config-next": "^12.2.5",
"eslint-plugin-simple-import-sort": "^8.0.0",
"@types/prettier": "^2.7.2",
"@types/ramda": "^0.28.22",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/react-table": "^7.7.14",
"eslint": "^8.33.0",
"eslint-config-next": "^13.1.6",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"prettier": "^2.7.1",
"jest": "^29.4.1",
"jest-environment-jsdom": "^29.4.1",
"prettier": "^2.8.3",
"pretty-quick": "^3.1.3",
"vscode-generate-index-standalone": "^1.6.0"
"vscode-generate-index-standalone": "^1.7.1"
},
"engines": {
"npm": "please-use-yarn",

View File

@ -1,4 +1,4 @@
import classNames from 'classnames/bind'
import classNames from 'classnames'
import styles from './Backdrop.module.scss'

View File

@ -1,4 +1,4 @@
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { AnimatedNumber, DisplayCurrency } from 'components/common'
import { lookup } from 'libs/parse'

View File

@ -1,4 +1,11 @@
import { useWallet } from '@marsprotocol/wallet-connector'
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import {
getChainInfo,
getClient,
useWallet,
useWalletManager,
WalletConnectionStatus,
} from '@marsprotocol/wallet-connector'
import { useQueryClient } from '@tanstack/react-query'
import { MARS_SYMBOL, USDC_SYMBOL } from 'constants/appConstants'
import {
@ -9,9 +16,10 @@ import {
useUserBalance,
useUserDebt,
useUserDeposit,
useUserIcns,
} from 'hooks/queries'
import { useSpotPrice } from 'hooks/queries/useSpotPrice'
import { ReactNode, useEffect } from 'react'
import { ReactNode, useEffect, useState } from 'react'
import useStore from 'store'
import { State } from 'types/enums'
import { Network } from 'types/enums/network'
@ -24,9 +32,15 @@ export const CommonContainer = ({ children }: CommonContainerProps) => {
// ------------------
// EXTERNAL HOOKS
// ---------------
const { chainInfo, address, signingCosmWasmClient, name } = useWallet()
const { recentWallet, simulate, sign, broadcast } = useWallet()
const { status } = useWalletManager()
const queryClient = useQueryClient()
const chainInfo = recentWallet?.network ? getChainInfo(recentWallet?.network.chainId) : undefined
const address = status !== WalletConnectionStatus.Connected ? '' : recentWallet?.account.address
const [cosmWasmClient, setCosmWasmClient] = useState<CosmWasmClient | undefined>()
// ------------------
// STORE STATE
// ------------------
@ -54,7 +68,6 @@ export const CommonContainer = ({ children }: CommonContainerProps) => {
const setClient = useStore((s) => s.setClient)
const setUserBalancesState = useStore((s) => s.setUserBalancesState)
const setUserWalletAddress = useStore((s) => s.setUserWalletAddress)
const setUserWalletName = useStore((s) => s.setUserWalletName)
// ------------------
// SETTERS
@ -75,21 +88,11 @@ export const CommonContainer = ({ children }: CommonContainerProps) => {
setUserWalletAddress(address || '')
}, [setUserWalletAddress, address])
useEffect(() => {
if (!name) return
setUserWalletName(name)
}, [setUserWalletName, name])
useEffect(() => {
if (!rpc || !chainID) return
setLcdClient(rpc, chainID)
}, [rpc, chainID, setLcdClient])
useEffect(() => {
if (!signingCosmWasmClient) return
setClient(signingCosmWasmClient)
}, [signingCosmWasmClient, setClient])
useEffect(() => {
if (userDebts && userDeposits && userBalances) {
setUserBalancesState(State.READY)
@ -98,6 +101,28 @@ export const CommonContainer = ({ children }: CommonContainerProps) => {
}
}, [userDebts, userDeposits, userBalances, setUserBalancesState])
useEffect(() => {
if (!recentWallet) return
if (!cosmWasmClient) {
const getCosmWasmClient = async () => {
const cosmClient = await getClient(recentWallet.network.rpc)
setCosmWasmClient(cosmClient)
}
getCosmWasmClient()
return
}
const client = {
broadcast,
cosmWasmClient,
recentWallet,
sign,
simulate,
}
setClient(client)
}, [simulate, sign, recentWallet, cosmWasmClient, broadcast, setClient])
useEffect(() => {
setRedBankAssets()
}, [
@ -126,6 +151,7 @@ export const CommonContainer = ({ children }: CommonContainerProps) => {
useBlockHeight()
useRedBank()
useUserBalance()
useUserIcns()
useUserDeposit()
useUserDebt()
useMarsOracle()

View File

@ -1,5 +1,6 @@
import React, { ReactNode, useEffect } from 'react'
import useStore from 'store'
import { AccountNftClient, CreditManagerClient } from 'types/classes'
interface FieldsContainerProps {
children: ReactNode
@ -9,15 +10,17 @@ export const FieldsContainer = ({ children }: FieldsContainerProps) => {
const client = useStore((s) => s.client)
const networkConfig = useStore((s) => s.networkConfig)
const userWalletAddress = useStore((s) => s.userWalletAddress)
const setAccountNftClient = useStore((s) => s.setAccountNftClient)
const setCreditManagerClient = useStore((s) => s.setCreditManagerClient)
const setCreditManagerMsgComposer = useStore((s) => s.setCreditManagerMsgComposer)
useEffect(() => {
if (!client) return
setAccountNftClient(client)
setCreditManagerClient(client)
}, [client, setAccountNftClient, setCreditManagerClient])
if (!client || !networkConfig) return
useStore.setState({
creditManagerClient: new CreditManagerClient(networkConfig?.contracts.creditManager, client),
})
useStore.setState({
accountNftClient: new AccountNftClient(networkConfig?.contracts.accountNft, client),
})
}, [client, networkConfig])
useEffect(() => {
if (!userWalletAddress || !networkConfig?.contracts.creditManager) return

View File

@ -1,152 +1,8 @@
@import 'src/styles/master';
.overlay {
background-color: $alphaBlack60;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
z-index: 50;
.loader {
display: flex;
align-items: center;
flex: 0 0 100%;
justify-content: center;
cursor: pointer;
margin: 0;
.loader {
display: flex;
flex: 0 0 100%;
justify-content: center;
@include margin(4, 0);
}
.button {
@include margin(4, 0, 0);
}
.content {
@include layoutTile;
width: rem-calc(540);
max-width: 100vw;
transform: translateX(-50%);
position: absolute;
left: 50%;
@include padding(4);
display: flex;
flex-direction: column;
outline: none;
cursor: auto;
.header {
@include typoXXLcaps;
text-align: center;
color: $fontColorLightPrimary;
font-weight: $fontWeightRegular;
@include margin(0, 0, 4);
}
.enableContent {
display: flex;
flex: 0 0 100%;
flex-wrap: wrap;
justify-content: center;
}
.text {
@include typoM;
text-align: center;
color: $fontColorLightSecondary;
width: 100%;
display: block;
button {
@include typoM;
appearance: none;
font-weight: $fontWeightSemibold;
color: $colorPrimary;
background-color: transparent;
border: none;
text-decoration: none;
&:hover {
text-decoration: underline;
cursor: pointer;
}
}
}
.list {
@include padding(2, 0);
display: flex;
flex-direction: column;
gap: space(4);
.wallet,
button,
a {
background: transparent;
@include padding(2);
box-shadow: none;
display: flex;
align-items: center;
appearance: none;
border: none;
width: 100%;
text-decoration: none;
border-radius: space(2);
cursor: pointer;
&.disabled {
pointer-events: none;
img,
.info .name {
opacity: 0.5;
}
}
&:hover {
background-color: $alphaWhite10;
}
img {
height: space(15);
width: space(15);
}
.info {
font-weight: $fontWeightRegular;
display: flex;
flex-direction: column;
margin-left: space(5);
.name {
@include typoLcaps;
color: $fontColorLightPrimary;
}
.description {
@include margin(1, 0, 0);
color: $fontColorLightTertiary;
text-align: left;
@include typoM;
&.capitalize {
text-transform: capitalize;
}
}
}
}
}
canvas {
max-width: 90vw;
max-height: 90vw;
height: rem-calc(320);
width: rem-calc(320);
border: space(3) solid $colorWhite;
margin: 0 auto;
}
}
@include margin(4, 0);
}

View File

@ -1,13 +1,6 @@
import { ChainInfoID, WalletManagerProvider, WalletType } from '@marsprotocol/wallet-connector'
import { ChainInfoID, WalletID, WalletManagerProvider } from '@marsprotocol/wallet-connector'
import { CircularProgress, SVG } from 'components/common'
import buttonStyles from 'components/common/Button/Button.module.scss'
import { NETWORK_CONFIG } from 'configs/osmo-test-4'
import { SESSION_WALLET_KEY } from 'constants/appConstants'
import KeplrImage from 'images/keplr-wallet-extension.png'
import WalletConnectImage from 'images/walletconnect-keplr.png'
import { FC } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import useStore from 'store'
import { useEffect, useState } from 'react'
import styles from './CosmosWalletConnectProvider.module.scss'
@ -15,66 +8,48 @@ type Props = {
children?: React.ReactNode
}
export const CosmosWalletConnectProvider: FC<Props> = ({ children }) => {
const { t } = useTranslation()
const chainId = useStore((s) => s.currentNetwork)
const defaultChain = ChainInfoID.OsmosisTestnet
export const CosmosWalletConnectProvider = ({ children }: Props) => {
const [chainInfoOverrides, setChainInfoOverrides] = useState<{ rpc: string; rest: string }>()
const [enabledWallets, setEnabledWallets] = useState<WalletID[]>([])
useEffect(() => {
if (chainInfoOverrides) return
const fetchConfig = async () => {
const file = await import(
`../../../configs/${
process.env.NEXT_PUBLIC_NETWORK === 'mainnet' ? 'osmosis-1' : 'osmo-test-4'
}.ts`
)
const networkConfig: NetworkConfig = file.NETWORK_CONFIG
setChainInfoOverrides({
rpc: networkConfig.rpcUrl,
rest: networkConfig.restUrl,
})
setEnabledWallets(networkConfig.wallets)
}
fetchConfig()
})
if (!chainInfoOverrides || !enabledWallets?.length) return null
return (
<WalletManagerProvider
chainInfoOverrides={{
[ChainInfoID.OsmosisTestnet]: {
rpc: NETWORK_CONFIG.rpcUrl,
rest: NETWORK_CONFIG.restUrl,
},
}}
classNames={{
modalContent: styles.content,
modalOverlay: styles.overlay,
modalHeader: styles.header,
modalCloseButton: styles.close,
walletList: styles.list,
wallet: styles.wallet,
walletImage: styles.image,
walletInfo: styles.info,
walletName: styles.name,
walletDescription: styles.description,
textContent: styles.text,
}}
chainInfoOverrides={chainInfoOverrides}
closeIcon={<SVG.Close />}
defaultChainId={chainId}
enabledWalletTypes={[WalletType.Keplr, WalletType.WalletConnectKeplr]}
enablingMeta={{
text: <Trans i18nKey='global.walletResetText' />,
textClassName: styles.text,
buttonText: <Trans i18nKey='global.walletReset' />,
buttonClassName: ` ${buttonStyles.button} ${buttonStyles.primary} ${buttonStyles.small} ${buttonStyles.solid} ${styles.button}`,
contentClassName: styles.enableContent,
}}
enablingStringOverride={t('global.connectingToWallet')}
localStorageKey={SESSION_WALLET_KEY}
defaultChainId={defaultChain}
enabledWallets={enabledWallets}
persistent
renderLoader={() => (
<div className={styles.loader}>
<CircularProgress size={30} />
</div>
)}
walletConnectClientMeta={{
name: 'Mars Protocol',
description:
'Lend, borrow and earn on the galaxy`s most powerful credit protocol or enter the Fields of Mars for advanced DeFi strategies.',
url: 'https://marsprotocol.io',
icons: ['https://marsprotocol.io/favicon.svg'],
}}
walletMetaOverride={{
[WalletType.Keplr]: {
description: <Trans i18nKey='global.keplrBrowserExtension' />,
imageUrl: KeplrImage.src,
},
[WalletType.WalletConnectKeplr]: {
name: <Trans i18nKey='global.walletConnect' />,
description: <Trans i18nKey='global.walletConnectDescription' />,
imageUrl: WalletConnectImage.src,
},
}}
>
{children}
</WalletManagerProvider>

View File

@ -1,8 +1,8 @@
import { useWallet, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import { ConnectButton, ConnectedButton } from 'components/common'
export const Connect = () => {
const { status } = useWallet()
const { status } = useWalletManager()
if (status === WalletConnectionStatus.Connected) return <ConnectedButton />

View File

@ -1,4 +1,4 @@
import { ChainInfoID, SimpleChainInfoList, useWalletManager } from '@marsprotocol/wallet-connector'
import { ChainInfoID, useWallet, useWalletManager } from '@marsprotocol/wallet-connector'
import { AnimatedNumber, Button, CircularProgress, DisplayCurrency, SVG } from 'components/common'
import { findByDenom } from 'functions'
import { useUserBalance } from 'hooks/queries'
@ -16,7 +16,8 @@ export const ConnectedButton = () => {
// ---------------
// EXTERNAL HOOKS
// ---------------
const { disconnect } = useWalletManager()
const { disconnect } = useWallet()
const { disconnect: terminate } = useWalletManager()
const { t } = useTranslation()
// ---------------
@ -25,7 +26,7 @@ export const ConnectedButton = () => {
const baseCurrency = useStore((s) => s.baseCurrency)
const chainInfo = useStore((s) => s.chainInfo)
const userWalletAddress = useStore((s) => s.userWalletAddress)
const userWalletName = useStore((s) => s.userWalletName)
const userIcns = useStore((s) => s.userIcns)
// ---------------
// LOCAL STATE
@ -34,18 +35,16 @@ export const ConnectedButton = () => {
successDuration: 1000 * 5,
})
const { data, isLoading } = useUserBalance()
// ---------------
// VARIABLES
// ---------------
const baseCurrencyBalance = Number(findByDenom(data || [], baseCurrency.denom || '')?.amount || 0)
const explorerName =
chainInfo && SimpleChainInfoList[chainInfo.chainId as ChainInfoID].explorerName
const explorerName = chainInfo ? chainInfo.explorerName : ''
const [showDetails, setShowDetails] = useState(false)
const viewOnFinder = useCallback(() => {
const explorerUrl = chainInfo && SimpleChainInfoList[chainInfo.chainId as ChainInfoID].explorer
const explorerUrl = chainInfo ? chainInfo.explorer : ''
window.open(`${explorerUrl}account/${userWalletAddress}`, '_blank')
}, [chainInfo, userWalletAddress])
@ -60,6 +59,11 @@ export const ConnectedButton = () => {
baseCurrency.decimals,
)
const handleDisconnect = () => {
disconnect()
terminate()
}
return (
<div className={styles.wrapper}>
{chainInfo?.chainId !== ChainInfoID.Osmosis1 && (
@ -75,18 +79,11 @@ export const ConnectedButton = () => {
text={
<>
<span className={styles.address}>
{userWalletName ? userWalletName : truncate(userWalletAddress, [2, 4])}
{userIcns ? userIcns.split('.')[0] : truncate(userWalletAddress, [2, 4])}
</span>
<span className={`${styles.balance} number`}>
{!isLoading ? (
`${formatValue(
currentBalanceAmount,
2,
2,
true,
false,
` ${chainInfo?.stakeCurrency?.coinDenom}`,
)}`
`${formatValue(currentBalanceAmount, 2, 2, true, false, ` ${baseCurrency.symbol}`)}`
) : (
<CircularProgress className={styles.circularProgress} size={12} />
)}
@ -99,26 +96,28 @@ export const ConnectedButton = () => {
<div className={styles.details}>
<div className={styles.detailsHeader}>
<div className={styles.detailsBalance}>
<div className={styles.detailsDenom}>{chainInfo?.stakeCurrency?.coinDenom}</div>
<div className={styles.detailsDenom}>{baseCurrency.symbol}</div>
<div className={`${styles.detailsBalanceAmount}`}>
<AnimatedNumber amount={currentBalanceAmount} abbreviated={false} />
<DisplayCurrency
className='s faded'
coin={{
amount: baseCurrencyBalance.toString(),
denom: baseCurrency.denom,
denom: baseCurrency.symbol,
}}
/>
</div>
</div>
<div className={styles.detailsButton}>
<Button color='secondary' onClick={disconnect} text={t('common.disconnect')} />
<Button
color='secondary'
onClick={handleDisconnect}
text={t('common.disconnect')}
/>
</div>
</div>
<div className={styles.detailsBody}>
<p className={styles.addressLabel}>
{userWalletName ? `${userWalletName}` : t('common.yourAddress')}
</p>
<p className={styles.addressLabel}>{userIcns ? userIcns : t('common.yourAddress')}</p>
<p className={styles.address}>{userWalletAddress}</p>
<p className={styles.addressMobile}>{truncate(userWalletAddress, [14, 14])}</p>
<div className={styles.buttons}>

View File

@ -63,7 +63,6 @@
.active {
opacity: 1;
pointer-events: none;
&:before {
content: '';
@ -74,6 +73,10 @@
background-color: $fontColorLightPrimary;
}
}
.unclickable {
pointer-events: none;
}
}
}

View File

@ -20,21 +20,28 @@ export const Header = () => {
<SVG.Logo />
</div>
<div className={styles.navbar}>
<Link href='/redbank' passHref>
<a className={classNames(styles.nav, !router.pathname.includes('farm') && styles.active)}>
{t('global.redBank')}
</a>
<Link
passHref
href='/redbank'
className={classNames(
styles.nav,
!router.pathname.includes('farm') && styles.active,
router.pathname === '/redbank' && styles.unclickable,
)}
>
{t('global.redBank')}
</Link>
<Link href='/farm' passHref>
<a
className={classNames(
!FIELDS_FEATURE && styles.disabled,
styles.nav,
router.pathname.includes('farm') && styles.active,
)}
>
{t('global.fields')}
</a>
<Link
passHref
href='/farm'
className={classNames(
!FIELDS_FEATURE && styles.disabled,
styles.nav,
router.pathname.includes('farm') && styles.active,
router.pathname === '/farm' && styles.unclickable,
)}
>
{t('global.fields')}
</Link>
<a className={styles.nav} href={networkConfig?.councilUrl} target='_blank' rel='noreferrer'>
{t('global.council')}

View File

@ -1,5 +1,4 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { ChainInfoID, SimpleChainInfoList } from '@marsprotocol/wallet-connector'
import { ChainInfoID, SimpleChainInfoList, TxBroadcastResult } from '@marsprotocol/wallet-connector'
import { useQueryClient } from '@tanstack/react-query'
import classNames from 'classnames'
import { AnimatedNumber, Button, DisplayCurrency, SVG, Tooltip, TxLink } from 'components/common'
@ -38,7 +37,7 @@ export const IncentivesButton = () => {
const [showDetails, setShowDetails] = useState(false)
const [disabled, setDisabled] = useState(true)
const [submitted, setSubmitted] = useState(false)
const [response, setResponse] = useState<ExecuteResult>()
const [response, setResponse] = useState<TxBroadcastResult>()
const [error, setError] = useState<string>()
const [hasUnclaimedRewards, setHasUnclaimedRewards] = useState(false)
@ -77,7 +76,7 @@ export const IncentivesButton = () => {
setSubmitted(false)
return
}
if (response?.transactionHash) {
if (response?.hash) {
setDisabled(true)
setSubmitted(false)
return
@ -156,8 +155,8 @@ export const IncentivesButton = () => {
<div className={`${styles.container} ${styles.info}`}>
<p className='m'>{t('incentives.successfullyClaimed')}</p>
<TxLink
hash={response?.transactionHash || ''}
link={`${explorerUrl}txs/${response?.transactionHash}`}
hash={response?.hash || ''}
link={`${explorerUrl}txs/${response?.hash}`}
/>
</div>
) : (

View File

@ -1,6 +1,6 @@
import { useWallet, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import BigNumber from 'bignumber.js'
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { Button, NumberInput, SVG, Toggle, Tooltip } from 'components/common'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -19,11 +19,11 @@ export const Settings = () => {
const [inputRef, setInputRef] = useState<React.RefObject<HTMLInputElement>>()
const [isCustom, setIsCustom] = useState(false)
const enableAnimations = useStore((s) => s.enableAnimations)
const { status } = useWallet()
const { status } = useWalletManager()
const onInputChange = (value: number) => {
setCustomSlippage(value.toString())
if (value.toString() === '') {
if (!value.toString()) {
return
}
}

View File

@ -1,4 +1,4 @@
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { ReactNode } from 'react'
import styles from './Highlight.module.scss'

View File

@ -1,4 +1,4 @@
import { useWallet, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import classNames from 'classnames'
import { Footer, Header, MobileNav } from 'components/common'
import { FieldsNotConnected } from 'components/fields'
@ -21,13 +21,11 @@ export const Layout = ({ children }: Props) => {
const enableAnimations = useStore((s) => s.enableAnimations)
const backgroundClasses = classNames('background', !userWalletAddress && 'night')
const vaultConfigs = useStore((s) => s.vaultConfigs)
const wallet = useWallet()
const wasConnectedBefore = !!localStorage.getItem(SESSION_WALLET_KEY)
const connectionSuccess = !!(
(wallet.status === WalletConnectionStatus.Connecting && wasConnectedBefore) ||
wallet.status === WalletConnectionStatus.Connected
)
const isConnected = !!userWalletAddress || connectionSuccess
const { status } = useWalletManager()
const isConnected = status === WalletConnectionStatus.Connected
const wasConnectedBefore =
localStorage.getItem(SESSION_WALLET_KEY) !== null &&
localStorage.getItem(SESSION_WALLET_KEY) !== '[]'
useEffect(() => {
if (!userWalletAddress) {
@ -41,7 +39,7 @@ export const Layout = ({ children }: Props) => {
<Header />
<div className='appContainer'>
<div className='widthBox'>
{isConnected ? (
{isConnected || wasConnectedBefore ? (
<div className={'body'}>{children}</div>
) : router.route.includes('farm') ? (
<FieldsNotConnected />

View File

@ -14,21 +14,25 @@ export const MobileNav = () => {
return (
<nav className={styles.mobileNav}>
<Link href='/redbank' passHref>
<a className={classNames(styles.nav, !router.pathname.includes('farm') && styles.active)}>
<SVG.RedBankIcon />
<span>{t('global.redBank')}</span>
</a>
<Link
href='/redbank'
passHref
className={classNames(styles.nav, !router.pathname.includes('farm') && styles.active)}
>
<SVG.RedBankIcon />
<span>{t('global.redBank')}</span>
</Link>
<a className={styles.nav} target='_blank' href={networkConfig?.councilUrl} rel='noreferrer'>
<SVG.CouncilIcon />
<span>{t('global.council')}</span>
</a>
<Link href='/farm' passHref>
<a className={classNames(styles.nav, router.pathname.includes('farm') && styles.active)}>
<SVG.FieldsIcon />
<span>{t('global.fields')}</span>
</a>
<Link
href='/farm'
passHref
className={classNames(styles.nav, router.pathname.includes('farm') && styles.active)}
>
<SVG.FieldsIcon />
<span>{t('global.fields')}</span>
</Link>
</nav>
)

View File

@ -110,7 +110,7 @@ export const NumberInput = (props: Props) => {
return
}
if (value === '') {
if (!value) {
updateValues(value, 0)
return
}

View File

@ -0,0 +1,23 @@
import Tippy from '@tippyjs/react'
interface Props {
text: string
tooltip: string
}
export const TextTooltip = (props: Props) => {
return (
<Tippy
appendTo={() => document.body}
animation={false}
render={(attrs) => {
return (
<div className='tippyContainer' {...attrs}>
{props.tooltip}
</div>
)
}}
>
<div>{props.text}</div>
</Tippy>
)
}

View File

@ -1,7 +1,6 @@
@import 'src/styles/master';
.container {
position: fixed;
display: flex;
flex-direction: column;
@include layoutTooltip;

View File

@ -1,5 +1,5 @@
import Tippy from '@tippyjs/react'
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { SVG } from 'components/common'
import { ReactNode } from 'react'

View File

@ -1,15 +1,14 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { decodeTxRaw } from '@cosmjs/proto-signing'
import { Coin } from '@cosmjs/stargate'
import { Card } from 'components/common'
import { TxFailedContent, TxSuccessContent } from 'components/common'
import { TxBroadcastResult } from '@marsprotocol/wallet-connector'
import { Card, TxFailedContent, TxSuccessContent } from 'components/common'
import { getFeeFromResponse } from 'functions'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import { TxStatus } from 'types/enums/RedBankAction'
interface Props {
response?: ExecuteResult
response?: TxBroadcastResult
error?: string
title: string
actions?: FieldsAction[]
@ -33,18 +32,18 @@ export const TxResponse = ({
const rpc = useStore((s) => s.chainInfo?.rpc)
const chainID = useStore((s) => s.chainInfo?.chainId)
const client = useStore((s) => s.client)
const baseCurrency = useStore((s) => s.baseCurrency)
useEffect(() => {
const getTxInfo = async (hash: string) => {
const getTxInfo = async (hash?: string) => {
if (txStatus !== TxStatus.LOADING) return
if (!rpc || !chainID) return
if (!rpc || !chainID || !hash || !response) return
try {
const txInfoResponse = await client?.getTx(hash)
const coin = getFeeFromResponse(response)
if (txInfoResponse) {
const decoded = decodeTxRaw(txInfoResponse?.tx)
setTxFee(decoded.authInfo.fee?.amount[0])
if (coin) {
setTxFee(coin)
}
setTxStatus(TxStatus.SUCCESS)
@ -56,8 +55,8 @@ export const TxResponse = ({
}
}
getTxInfo(response?.transactionHash || '')
}, [client, response, rpc, chainID, checkTxStatus, onSuccess, txStatus])
getTxInfo(response?.hash || undefined)
}, [client, response, rpc, chainID, checkTxStatus, onSuccess, txStatus, baseCurrency.denom])
// reset scroll
useEffect(() => {
@ -79,7 +78,7 @@ export const TxResponse = ({
{txStatus === TxStatus.FAILURE ? (
<TxFailedContent
handleClose={handleClose}
hash={response?.transactionHash || ''}
hash={response?.response.transactionHash || ''}
message={error}
/>
) : (

View File

@ -1,8 +1,6 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { Coin } from '@cosmjs/stargate'
import { ChainInfoID, SimpleChainInfoList } from '@marsprotocol/wallet-connector'
import { Button, TxFee, TxLink } from 'components/common'
import { InfoTitle } from 'components/common'
import { ChainInfoID, SimpleChainInfoList, TxBroadcastResult } from '@marsprotocol/wallet-connector'
import { Button, InfoTitle, TxFee, TxLink } from 'components/common'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import { TxStatus } from 'types/enums/RedBankAction'
@ -11,7 +9,7 @@ import styles from './TxSuccessContent.module.scss'
interface Props {
title: string
response?: ExecuteResult
response?: TxBroadcastResult | null
txFee?: Coin
txStatus: TxStatus
actions: { label: string; values: string[] }[]
@ -68,8 +66,8 @@ export const TxSuccessContent = ({
<div className={styles.item}>
<div className={styles.label}>{t('common.txHash')}</div>
<TxLink
hash={response?.transactionHash || ''}
link={`${explorerUrl}txs/${response?.transactionHash}`}
hash={response?.response.transactionHash || ''}
link={`${explorerUrl}txs/${response?.response.transactionHash}`}
/>
</div>
</div>

View File

@ -28,6 +28,7 @@ export { MobileNav } from './MobileNav/MobileNav'
export { Notification } from './Notification/Notification'
export { NumberInput } from './NumberInput/NumberInput'
export { SVG } from './SVG/SVG'
export { TextTooltip } from './TextTooltip/TextTooltip'
export { Title } from './Title/Title'
export { Toggle } from './Toggle/Toggle'
export { TokenBalance } from './TokenBalance/TokenBalance'

View File

@ -1,5 +1,17 @@
@import 'src/styles/master';
$color: ':nth-child(1)';
$logo: ':nth-child(2)';
$name: ':nth-child(3)';
$posValue: ':nth-child(4)';
$netValue: ':nth-child(5)';
$borrowValue: ':nth-child(6)';
$vaultCap: ':nth-child(7)';
$apy: ':nth-child(8)';
$leverage: ':nth-child(9)';
$borrowCapacity: ':nth-child(10)';
$actions: ':nth-child(11)';
.table {
display: none;
@include margin(0, 0, 6, 0);
@ -32,13 +44,11 @@
}
}
// Not color indicator
&:not(:first-child) {
&:not(#{$color}) {
@include padding(4, 2);
}
// Name
&:nth-child(3) {
&#{$name} {
@include padding(4, 2, 4, 0);
}
}
@ -64,24 +74,15 @@
.td {
position: relative;
// Logos
&:nth-child(2) {
&#{$logo} {
width: rem-calc(64);
}
// Not color indicator
&:not(:first-child) {
&:not(#{$color}) {
@include padding(0, 2);
}
// Name
&:nth-child(3) {
text-align: left;
@include padding(0, 2, 0, 0);
}
// Leverage
&:nth-child(3) {
&#{$name} {
text-align: left;
@include padding(0, 2, 0, 0);
}
@ -92,11 +93,9 @@
.table {
.td,
.th {
// Leverage
&:nth-child(5),
&:nth-child(6),
&:nth-child(8),
&:nth-child(9) {
&#{$vaultCap},
&#{$borrowValue},
&#{$leverage} {
display: none;
}
}

View File

@ -68,7 +68,7 @@ export const ActiveVaultsTable = () => {
title={t('fields.activeVaults')}
hideHeaderBorder
styleOverride={{ marginBottom: 40 }}
tooltip={<Trans i18nKey='fields.tooltips.activeVaults' />}
tooltip={<Trans i18nKey='fields.tooltips.activeVaults.desktop' />}
>
<table className={styles.table}>
<thead className={styles.thead}>

View File

@ -4,6 +4,11 @@
display: flex;
flex-direction: column;
.link {
color: unset;
text-decoration: none;
}
.grid {
display: grid;
grid-template-columns: rem-calc(64) 1fr 1fr;

View File

@ -1,8 +1,8 @@
import Tippy from '@tippyjs/react'
import BigNumber from 'bignumber.js'
import { BorrowCapacity, Card, DisplayCurrency } from 'components/common'
import { AnimatedNumber, Apy, BorrowCapacity, Card, DisplayCurrency } from 'components/common'
import { VaultLogo, VaultName } from 'components/fields'
import { FIELDS_TUTORIAL_KEY } from 'constants/appConstants'
import { formatValue } from 'libs/parse'
import Link from 'next/link'
import { Trans, useTranslation } from 'react-i18next'
import useStore from 'store'
@ -45,7 +45,7 @@ export const ActiveVaultsTableMobile = () => {
<Card
title={t('fields.activeVaults')}
styleOverride={{ marginBottom: 40 }}
tooltip={<Trans i18nKey='fields.tooltips.activeVaults' />}
tooltip={<Trans i18nKey='fields.tooltips.activeVaults.mobile' />}
>
<div className={styles.container}>
{activeVaults.map((vault, i) => {
@ -55,24 +55,39 @@ export const ActiveVaultsTableMobile = () => {
<VaultLogo vault={vault} />
</div>
<div className={styles.name}>{getVaultSubText(vault)}</div>
<div className={styles.position}>
<DisplayCurrency
coin={{
denom: baseCurrency.denom,
amount: vault.position.values.total.toString(),
}}
className={'xl'}
/>
<div className='xl' onClick={(e) => e.preventDefault()}>
<span className='faded'>{t('common.apy')} </span>
<span>
<Tippy
content={
<Apy
apyData={{
borrow: vault.position.apy.borrow,
total: vault.position.apy.total,
}}
leverage={vault.position.currentLeverage}
/>
}
>
<span className='tooltip xl'>
<AnimatedNumber amount={vault.position.apy.net} className='xl' suffix='%' />
</span>
</Tippy>
</span>
</div>
<div className='s'>
<span className='faded'>{t('common.debt')} </span>
<span className='faded'>{t('fields.positionValueShort')} </span>
<DisplayCurrency
coin={{
denom: baseCurrency.denom,
amount: vault.position.values.borrowed.toString(),
amount: vault.position.values.total.toString(),
}}
className={styles.inline}
className={`s ${styles.inline}`}
/>
</div>
<div className='s'>
<span className='faded'>{t('common.net')} </span>
<DisplayCurrency
@ -84,20 +99,18 @@ export const ActiveVaultsTableMobile = () => {
/>
</div>
<div className='s'>
<span className='faded'>{t('fields.leverage')} </span>
{new BigNumber(vault.position.currentLeverage).toPrecision(3)}
<span className='faded'>{t('common.debt')} </span>
<DisplayCurrency
coin={{
denom: baseCurrency.denom,
amount: vault.position.values.borrowed.toString(),
}}
className={styles.inline}
/>
</div>
<div className='s'>
<span className='faded'>{t('fields.vaultCap')} </span>
{formatValue(
(vault.vaultCap?.max || 0) / 10 ** baseCurrency.decimals,
0,
0,
true,
'',
` ${baseCurrency.symbol}`,
)}
<span className='faded'>{t('fields.leverage')} </span>
{new BigNumber(vault.position.currentLeverage).toPrecision(3)}
</div>
</div>
<div className={styles.borrowCapacity}>
@ -124,6 +137,7 @@ export const ActiveVaultsTableMobile = () => {
href={`/farm/vault/${vault.address}/${
vault.position.status === 'active' ? 'edit' : 'close'
}`}
className={styles.link}
>
<div>{content}</div>
</Link>

View File

@ -1,20 +1,28 @@
import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import Tippy from '@tippyjs/react'
import BigNumber from 'bignumber.js'
import classNames from 'classnames/bind'
import classNames from 'classnames'
import {
AnimatedNumber,
Apy,
BorrowCapacity,
Button,
DisplayCurrency,
SVG,
TextTooltip,
TokenBalance,
} from 'components/common'
import { VaultLogo, VaultName } from 'components/fields'
import { VAULT_DEPOSIT_BUFFER } from 'constants/appConstants'
import { convertPercentage } from 'functions'
import { getLiqBorrowValue, getMaxBorrowValue } from 'functions/fields'
import { convertApyToDailyApy, formatUnlockDate, formatValue, ltvToLeverage } from 'libs/parse'
import {
convertApyToDailyApy,
formatUnlockDate,
formatValue,
getTimeAndUnit,
ltvToLeverage,
} from 'libs/parse'
import { useRouter } from 'next/router'
import { ReactNode, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@ -43,11 +51,38 @@ export const useActiveVaultsColumns = () => {
columnHelper.accessor('name', {
enableSorting: true,
header: t('fields.position'),
cell: ({ row }) => <VaultName vault={row.original} />,
cell: ({ row }) => {
return (
<Tippy
appendTo={() => document.body}
animation={false}
render={(attrs) => {
return (
<div className='tippyContainer' {...attrs}>
{t('fields.tooltips.name', {
asset1: row.original.symbols.primary,
asset2: row.original.symbols.secondary,
...getTimeAndUnit(row.original.lockup),
})}
</div>
)
}}
>
<div>
<VaultName vault={row.original} />
</div>
</Tippy>
)
},
}),
columnHelper.accessor('position.values.total', {
enableSorting: true,
header: t('fields.positionValueShort'),
header: () => (
<TextTooltip
text={t('fields.positionValueShort')}
tooltip={t('fields.tooltips.positionValue')}
/>
),
cell: ({ row }) => {
const primaryCoin = {
denom: row.original.denoms.primary,
@ -89,7 +124,9 @@ export const useActiveVaultsColumns = () => {
}),
columnHelper.accessor('position.values.primary', {
enableSorting: true,
header: t('fields.netValue'),
header: () => (
<TextTooltip text={t('fields.netValue')} tooltip={t('fields.tooltips.netValue')} />
),
cell: ({ row }) => {
const position = row.original.position
const netValue = position.values.primary + position.values.secondary
@ -129,7 +166,9 @@ export const useActiveVaultsColumns = () => {
}),
columnHelper.accessor('position.values.borrowed', {
enableSorting: true,
header: t('common.borrowed'),
header: () => (
<TextTooltip text={t('common.borrowed')} tooltip={t('fields.tooltips.borrowValue')} />
),
cell: (info) => {
const borrowAsset = whitelistedAssets.find(
(asset) => asset.denom === info.row.original.denoms.secondary,
@ -169,7 +208,9 @@ export const useActiveVaultsColumns = () => {
}),
columnHelper.accessor('vaultCap', {
enableSorting: true,
header: t('fields.vaultCap'),
header: () => (
<TextTooltip text={t('fields.vaultCap')} tooltip={t('fields.tooltips.vaultCap')} />
),
cell: ({ row }) => {
if (!row.original.vaultCap) {
return null
@ -217,7 +258,9 @@ export const useActiveVaultsColumns = () => {
columnHelper.accessor('position.apy', {
id: 'apy',
enableSorting: true,
header: t('common.apy'),
header: () => (
<TextTooltip text={t('common.apy')} tooltip={t('fields.tooltips.apy.active')} />
),
cell: ({ row }) => {
switch (row.original.position.status) {
case 'unlocked':
@ -228,13 +271,28 @@ export const useActiveVaultsColumns = () => {
</>
)
case 'active':
const apy = new BigNumber(row.original.position.apy).decimalPlaces(2).toNumber()
const apy = new BigNumber(row.original.position.apy.net).decimalPlaces(2).toNumber()
const apyData = {
total: row.original.apy || 0,
borrow: row.original.position.apy.borrow,
}
return (
<>
<AnimatedNumber amount={apy} className='m' suffix='%' />
<p className='s faded'>
{convertApyToDailyApy(row.original.position.apy)}%/{t('common.day')}
</p>
<Tippy
appendTo={() => document.body}
animation={false}
content={
<Apy apyData={apyData} leverage={row.original.position.currentLeverage} />
}
>
<span className='tooltip'>
<AnimatedNumber amount={apy} className='m' suffix='%' />
<p className='s faded'>
{convertApyToDailyApy(row.original.position.apy.net)}%/{t('common.day')}
</p>
</span>
</Tippy>
</>
)
case 'unlocking':
@ -242,7 +300,6 @@ export const useActiveVaultsColumns = () => {
<>
<p className='m'>{t('fields.unlocking')}</p>
<p className='s faded'>
{/* {new Date(row.original.position?.unlockAtTimestamp || 0).} */}
{formatUnlockDate(row.original.position?.unlockAtTimestamp || 0)}
</p>
</>
@ -252,7 +309,9 @@ export const useActiveVaultsColumns = () => {
}),
columnHelper.accessor('ltv', {
enableSorting: true,
header: t('fields.leverage'),
header: () => (
<TextTooltip text={t('fields.leverage')} tooltip={t('fields.tooltips.leverage.active')} />
),
cell: ({ row }) => {
return (
<>
@ -275,7 +334,12 @@ export const useActiveVaultsColumns = () => {
}),
columnHelper.accessor('position.amounts.borrowed', {
enableSorting: false,
header: t('common.borrowingCapacity'),
header: () => (
<TextTooltip
text={t('common.borrowingCapacity')}
tooltip={t('fields.tooltips.borrowCapacity')}
/>
),
cell: ({ row }) => {
const maxBorrowValue = getMaxBorrowValue(row.original, row.original.position)

View File

@ -47,8 +47,6 @@ $action: ':nth-child(9)';
}
}
$number: 1;
&:not(#{$color}) {
@include padding(4, 2);
}
@ -111,7 +109,7 @@ $action: ':nth-child(9)';
&#{$description} {
text-align: left;
width: rem-calc(300);
@include padding(0, 2, 0, 0);
@include padding(2, 2, 2, 0);
}
}
}

View File

@ -53,7 +53,7 @@ export const AvailableVaultsTable = () => {
title={t('fields.availableVaults')}
hideHeaderBorder
styleOverride={{ marginBottom: 40 }}
tooltip={<Trans i18nKey='fields.tooltips.activeVaults' />}
tooltip={<Trans i18nKey='fields.tooltips.availableVaults.desktop' />}
>
<table className={styles.table}>
<thead className={styles.thead}>
@ -68,7 +68,7 @@ export const AvailableVaultsTable = () => {
const wrapperClasses = classes({
wrapper: true,
left: header.id === 'name' || header.id === 'description',
center: header.id === 'provider_name',
center: header.id === 'provider',
})
return (

View File

@ -4,6 +4,15 @@
display: flex;
flex-direction: column;
.inline {
display: inline;
}
.link {
color: unset;
text-decoration: none;
}
.grid {
display: grid;
grid-template-columns: rem-calc(64) 1fr 1fr;

View File

@ -1,7 +1,9 @@
import Tippy from '@tippyjs/react'
import BigNumber from 'bignumber.js'
import { AnimatedNumber, Card } from 'components/common'
import { AnimatedNumber, Apy, Card, DisplayCurrency } from 'components/common'
import { VaultLogo, VaultName } from 'components/fields'
import { formatValue, ltvToLeverage } from 'libs/parse'
import { getTimeAndUnit, ltvToLeverage } from 'libs/parse'
import Link from 'next/link'
import React from 'react'
import { Trans, useTranslation } from 'react-i18next'
import useStore from 'store'
@ -12,6 +14,7 @@ export const AvailableVaultsTableMobile = () => {
const { t } = useTranslation()
const availableVaults = useStore((s) => s.availableVaults)
const baseCurrency = useStore((s) => s.baseCurrency)
const redBankAssets = useStore((s) => s.redBankAssets)
if (!availableVaults?.length) return null
@ -19,49 +22,78 @@ export const AvailableVaultsTableMobile = () => {
<Card
title={t('fields.availableVaults')}
styleOverride={{ marginBottom: 40 }}
tooltip={<Trans i18nKey='fields.tooltips.availableVaults' />}
tooltip={<Trans i18nKey='fields.tooltips.availableVaults.mobile' />}
>
<div className={styles.container}>
{availableVaults.map((vault, i) => {
const minAPY = new BigNumber(vault.apy || 0).decimalPlaces(2).toNumber()
const maxAPY = new BigNumber(minAPY).times(ltvToLeverage(vault.ltv.max)).toNumber()
const borrowAsset = redBankAssets.find((asset) => asset.denom === vault.denoms.secondary)
const maxBorrowRate = Number(borrowAsset?.borrowRate ?? 0) * vault.ltv.max
const minAPY = new BigNumber(vault.apy || 0).toNumber()
const leverage = ltvToLeverage(vault.ltv.max)
const maxAPY =
new BigNumber(minAPY).times(leverage).decimalPlaces(2).toNumber() - maxBorrowRate
const apyDataNoLev = { total: vault.apy || 0, borrow: 0 }
const apyDataLev = { total: vault.apy || 0, borrow: maxBorrowRate }
return (
<div className={styles.grid} key={`${vault.address}-${i}`}>
<div className={styles.logo}>
<VaultLogo vault={vault} />
</div>
<div className={styles.name}>
<VaultName vault={vault} />
</div>
<div className={styles.stats}>
<div className='xl'>
<span className='faded'>{t('common.apy')} </span>
<span>
<AnimatedNumber amount={minAPY} suffix='-' />
<AnimatedNumber amount={maxAPY} suffix='%' />
</span>
<Link
key={`${vault.address}-${i}`}
href={`/farm/vault/${vault.address}/create`}
className={styles.link}
>
<div className={styles.grid} key={`${vault.address}-${i}`}>
<div className={styles.logo}>
<VaultLogo vault={vault} />
</div>
<div className='s'>
<span className='faded'>{t('fields.leverage')} </span>
<AnimatedNumber amount={leverage} suffix='x' />
<div className={styles.name}>
<VaultName vault={vault} />
</div>
<div className='s'>
<span className='faded'>{t('fields.vaultCap')} </span>
<span>
{formatValue(
(vault.vaultCap?.max || 0) / 10 ** baseCurrency.decimals,
0,
0,
true,
'',
' ' + baseCurrency.symbol,
)}
</span>
<div className={styles.stats}>
<div onClick={(e) => e.preventDefault()} className='xl'>
<span className='faded'>{t('common.apy')} </span>
<span>
<Tippy content={<Apy apyData={apyDataNoLev} leverage={1} />}>
<span className='tooltip xl'>
<AnimatedNumber amount={minAPY} suffix='-' />
</span>
</Tippy>
<Tippy
content={
<Apy apyData={apyDataLev} leverage={ltvToLeverage(vault.ltv.max)} />
}
>
<span className='tooltip xl'>
<AnimatedNumber amount={maxAPY} suffix='%' />
</span>
</Tippy>
</span>
</div>
<div className='s'>
<span className='faded'>{t('fields.leverage')} </span>
<AnimatedNumber amount={leverage} suffix='x' />
</div>
<div className='s'>
<span className='faded'>{t('fields.vaultCap')} </span>
<span>
<DisplayCurrency
coin={{
denom: baseCurrency.denom,
amount: (vault.vaultCap?.max || 0).toString(),
}}
className={styles.inline}
/>
</span>
</div>
</div>
<div className={styles.description}>
{t('fields.tooltips.name', {
asset1: vault.symbols.primary,
asset2: vault.symbols.secondary,
...getTimeAndUnit(vault.lockup),
})}
</div>
</div>
<div className={styles.description}>{vault.description}</div>
</div>
</Link>
)
})}
</div>

View File

@ -4,6 +4,6 @@
position: absolute;
top: 0;
width: rem-calc(7);
height: space(16);
height: 100%;
margin-left: rem-calc(-7);
}

View File

@ -1,12 +1,20 @@
import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import Tippy from '@tippyjs/react'
import BigNumber from 'bignumber.js'
import classNames from 'classnames/bind'
import { AnimatedNumber, Button, DisplayCurrency, SVG, TokenBalance } from 'components/common'
import classNames from 'classnames'
import {
AnimatedNumber,
Apy,
Button,
DisplayCurrency,
SVG,
TextTooltip,
TokenBalance,
} from 'components/common'
import { VaultLogo, VaultName } from 'components/fields'
import { VAULT_DEPOSIT_BUFFER } from 'constants/appConstants'
import { convertPercentage } from 'functions'
import { convertApyToDailyApy, formatValue, ltvToLeverage } from 'libs/parse'
import { convertApyToDailyApy, formatValue, getTimeAndUnit, ltvToLeverage } from 'libs/parse'
import { useRouter } from 'next/router'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@ -19,6 +27,8 @@ export const useAvailableVaultsColumns = () => {
const router = useRouter()
const baseCurrency = useStore((s) => s.baseCurrency)
const columnHelper = createColumnHelper<Vault>()
const redBankAssets = useStore((s) => s.redBankAssets)
const defaultAvailableVaultsColumns: ColumnDef<Vault, any>[] = useMemo(
() => [
columnHelper.accessor('color', {
@ -33,12 +43,39 @@ export const useAvailableVaultsColumns = () => {
}),
columnHelper.accessor('name', {
enableSorting: true,
header: t('fields.position'),
cell: ({ row }) => <VaultName vault={row.original} />,
header: t('fields.name'),
cell: ({ row }) => {
return (
<Tippy
appendTo={() => document.body}
animation={false}
render={(attrs) => {
return (
<div className='tippyContainer' {...attrs}>
{t('fields.tooltips.name', {
asset1: row.original.symbols.primary,
asset2: row.original.symbols.secondary,
...getTimeAndUnit(row.original.lockup),
})}
</div>
)
}}
>
<div>
<VaultName vault={row.original} />
</div>
</Tippy>
)
},
}),
columnHelper.accessor('ltv', {
enableSorting: true,
header: t('fields.leverage'),
header: () => (
<TextTooltip
text={t('fields.leverage')}
tooltip={t('fields.tooltips.leverage.available')}
/>
),
cell: ({ row }) => {
return (
<>
@ -53,16 +90,22 @@ export const useAvailableVaultsColumns = () => {
columnHelper.accessor('apy', {
id: 'apy',
enableSorting: true,
header: t('common.apy'),
header: () => (
<TextTooltip text={t('common.apy')} tooltip={t('fields.tooltips.apy.available')} />
),
cell: ({ row }) => {
if (!row.original.apy) {
return null
}
const maxLeverage = ltvToLeverage(row.original.ltv.max)
const borrowAsset = redBankAssets.find(
(asset) => asset.denom === row.original.denoms.secondary,
)
const maxBorrowRate = Number(borrowAsset?.borrowRate ?? 0) * row.original.ltv.max
const minAPY = new BigNumber(row.original.apy).decimalPlaces(2).toNumber()
const maxAPY = new BigNumber(minAPY).times(maxLeverage).decimalPlaces(2).toNumber()
const maxAPY = new BigNumber(minAPY).times(maxLeverage).toNumber() - maxBorrowRate
const minDailyAPY = new BigNumber(convertApyToDailyApy(row.original.apy))
.decimalPlaces(2)
.toNumber()
@ -70,13 +113,27 @@ export const useAvailableVaultsColumns = () => {
.times(maxLeverage)
.decimalPlaces(2)
.toNumber()
const apyDataNoLev = { total: row.original.apy || 0, borrow: 0 }
const apyDataLev = { total: row.original.apy || 0, borrow: maxBorrowRate }
return (
<>
<p className='m'>
<AnimatedNumber amount={minAPY} />
<span>-</span>
<AnimatedNumber amount={maxAPY} suffix='%' />
</p>
<Tippy content={<Apy apyData={apyDataNoLev} leverage={1} />}>
<span className='tooltip m'>
<AnimatedNumber amount={minAPY} />
</span>
</Tippy>
<span>-</span>
<Tippy
content={
<Apy apyData={apyDataLev} leverage={ltvToLeverage(row.original.ltv.max)} />
}
>
<span className='tooltip ,'>
<AnimatedNumber amount={maxAPY} suffix='%' />
</span>
</Tippy>
<p className='s faded'>
{minDailyAPY}-{maxDailyAPY}%/
{t('common.day')}
@ -87,7 +144,9 @@ export const useAvailableVaultsColumns = () => {
}),
columnHelper.accessor('vaultCap', {
enableSorting: true,
header: t('fields.vaultCap'),
header: () => (
<TextTooltip text={t('fields.vaultCap')} tooltip={t('fields.tooltips.vaultCap')} />
),
cell: ({ row }) => {
if (!row.original.vaultCap) {
return null
@ -140,7 +199,25 @@ export const useAvailableVaultsColumns = () => {
columnHelper.accessor('description', {
enableSorting: true,
header: t('common.description'),
cell: ({ row }) => <p>{row.original.description}</p>,
cell: ({ row }) => (
<Tippy
appendTo={() => document.body}
animation={false}
render={(attrs) => {
return (
<div className='tippyContainer' {...attrs}>
{t('fields.tooltips.name', {
asset1: row.original.symbols.primary,
asset2: row.original.symbols.secondary,
...getTimeAndUnit(row.original.lockup),
})}
</div>
)
}}
>
<p>{row.original.description}</p>
</Tippy>
),
}),
columnHelper.display({
id: 'actions',

View File

@ -1,4 +1,4 @@
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { BreakdownGraph, BreakdownTable } from 'components/fields'
import React from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -1,5 +1,5 @@
import Tippy from '@tippyjs/react'
import classNames from 'classnames/bind'
import classNames from 'classnames'
import styles from './AssetBar.module.scss'
@ -12,11 +12,11 @@ interface Props {
}
export const AssetBar = (props: Props) => {
const labelClasses = classNames([
const labelClasses = classNames(
styles.label,
props.alignRight && styles.alignRight,
props.showLabel ? styles.show : styles.hide,
])
)
return (
<Tippy
content={

View File

@ -1,8 +1,7 @@
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { DisplayCurrency } from 'components/common'
import { AssetBar } from 'components/fields'
import { lookup } from 'libs/parse'
import React from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'

View File

@ -1,5 +1,5 @@
import Tippy from '@tippyjs/react'
import classNames from 'classnames/bind'
import classNames from 'classnames'
import {
AnimatedNumber,
Apy,

View File

@ -1,16 +1,13 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { TxResponse } from 'components/common'
import { useFieldsActionMessages } from 'hooks/data'
import { useRouter } from 'next/router'
import React from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import styles from './ClosePositionResponse.module.scss'
interface Props {
data?: ExecuteResult
error: Error | null
data?: ResultData
isLoading: boolean
accountId: string
}
@ -20,7 +17,7 @@ export const ClosePositionResponse = (props: Props) => {
const router = useRouter()
const getVaults = useStore((s) => s.getVaults)
const { repayMessage, withdrawMessage } = useFieldsActionMessages(props.data)
const { repayMessage, withdrawMessage } = useFieldsActionMessages(props.data?.result)
const actions = [
...(repayMessage ? [repayMessage] : []),
@ -30,12 +27,12 @@ export const ClosePositionResponse = (props: Props) => {
return (
<div className={styles.container}>
<TxResponse
error={props.error?.message}
error={props.data?.error}
handleClose={() => router.replace('/farm')}
onSuccess={() => {
getVaults({ refetch: true })
}}
response={props.data}
response={props.data?.result}
title={t('fields.txMessages.close')}
actions={actions}
/>

View File

@ -1,40 +1,38 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { TxResponse } from 'components/common'
import { useFieldsActionMessages } from 'hooks/data'
import { useRouter } from 'next/router'
import React from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import styles from './EditResponse.module.scss'
interface Props {
data?: ExecuteResult
error: Error | null
data?: ResultData
isLoading: boolean
activeVault: ActiveVault
}
export const EditResponse = (props: Props) => {
const { t } = useTranslation()
const getVaults = useStore((s) => s.getVaults)
const router = useRouter()
const { depositMessage, borrowMessage, swapMessage } = useFieldsActionMessages(props.data)
const getVaults = useStore((s) => s.getVaults)
const { depositMessage, borrowMessage, swapMessage } = useFieldsActionMessages(props.data?.result)
const actions = [
...(depositMessage ? [depositMessage] : []),
...(borrowMessage ? [borrowMessage] : []),
...(swapMessage ? [swapMessage] : []),
]
return (
<div className={styles.container}>
<TxResponse
error={props.error?.message}
error={props.data?.error}
handleClose={() => router.replace('/farm')}
onSuccess={() => {
getVaults({ refetch: true })
}}
response={props.data}
response={props.data?.result}
title={t('fields.txMessages.edit')}
actions={actions}
/>

View File

@ -1,4 +1,4 @@
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { Tutorial } from 'components/common'
import { TokenInput } from 'components/fields'
import { FIELDS_TUTORIAL_KEY } from 'constants/appConstants'

View File

@ -1,5 +1,5 @@
import BigNumber from 'bignumber.js'
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { InputSlider, Tutorial } from 'components/common'
import { FIELDS_TUTORIAL_KEY } from 'constants/appConstants'
import { formatValue, ltvToLeverage } from 'libs/parse'

View File

@ -1,4 +1,4 @@
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { Button, SVG, Tutorial } from 'components/common'
import { TokenInput } from 'components/fields'
import { FIELDS_TUTORIAL_KEY } from 'constants/appConstants'

View File

@ -1,5 +1,5 @@
import { Coin } from '@cosmjs/proto-signing'
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { Button, DisplayCurrency, NumberInput } from 'components/common'
import { findByDenom } from 'functions'
import { useAsset } from 'hooks/data'

View File

@ -1,16 +1,13 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { TxResponse } from 'components/common'
import { useFieldsActionMessages } from 'hooks/data'
import { useRouter } from 'next/router'
import React from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import styles from './RepayResponse.module.scss'
interface Props {
data?: ExecuteResult
error: Error | null
data?: ResultData
isLoading: boolean
vault: ActiveVault
}
@ -19,19 +16,19 @@ export const RepayResponse = (props: Props) => {
const { t } = useTranslation()
const getVaults = useStore((s) => s.getVaults)
const router = useRouter()
const { repayMessage } = useFieldsActionMessages(props.data)
const { repayMessage } = useFieldsActionMessages(props.data?.result)
const actions = [...(repayMessage ? [repayMessage] : [])]
return (
<div className={styles.container}>
<TxResponse
error={props.error?.message}
error={props.data?.error}
handleClose={() => router.replace('/farm')}
onSuccess={() => {
getVaults({ refetch: true })
}}
response={props.data}
response={props.data?.result}
title={t('fields.txMessages.repay')}
actions={actions}
/>

View File

@ -1,16 +1,13 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { TxResponse } from 'components/common'
import { useFieldsActionMessages } from 'hooks/data'
import { useRouter } from 'next/router'
import React from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import styles from './SetUpResponse.module.scss'
interface Props {
data?: ExecuteResult
error: Error | null
data?: ResultData
isLoading: boolean
accountId: string
}
@ -18,8 +15,8 @@ interface Props {
export const SetUpResponse = (props: Props) => {
const { t } = useTranslation()
const router = useRouter()
const { depositMessage, borrowMessage, swapMessage } = useFieldsActionMessages(props.data)
const getVaults = useStore((s) => s.getVaults)
const { depositMessage, borrowMessage, swapMessage } = useFieldsActionMessages(props.data?.result)
const actions = [
...(depositMessage ? [depositMessage] : []),
@ -30,12 +27,12 @@ export const SetUpResponse = (props: Props) => {
return (
<div className={styles.container}>
<TxResponse
error={props.error?.message}
error={props.data?.error}
handleClose={() => router.replace('/farm')}
onSuccess={() => {
getVaults({ refetch: true })
}}
response={props.data}
response={props.data?.result}
title={t('fields.txMessages.setup')}
actions={actions}
/>

View File

@ -1,16 +1,13 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { TxResponse } from 'components/common'
import { useFieldsActionMessages } from 'hooks/data'
import { useRouter } from 'next/router'
import React from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import styles from './UnlockResponse.module.scss'
interface Props {
data?: ExecuteResult
error: Error | null
data?: ResultData
isLoading: boolean
activeVault: ActiveVault
}
@ -19,15 +16,15 @@ export const UnlockResponse = (props: Props) => {
const { t } = useTranslation()
const getVaults = useStore((s) => s.getVaults)
const router = useRouter()
const { unlockMessages } = useFieldsActionMessages(props.data, props.activeVault)
const { unlockMessages } = useFieldsActionMessages(props.data?.result, props.activeVault)
return (
<div className={styles.container}>
<TxResponse
error={props.error?.message}
error={props.data?.error}
handleClose={() => router.replace('/farm')}
onSuccess={() => getVaults({ refetch: true })}
response={props.data}
response={props.data?.result}
title={t('fields.txMessages.unlock')}
actions={unlockMessages}
/>

View File

@ -58,7 +58,7 @@ export const ActionsRow = ({ row, type }: Props) => {
<div className='tippyContainer' {...attrs}>
{t('redbank.toDepositAssetOnChain', {
asset: assetSymbol,
chain: chainInfo?.chainName,
chain: chainInfo?.name,
})}
</div>
)

View File

@ -27,7 +27,7 @@ export const useBorrowColumns = () => {
header: '',
cell: (info) => (
<div className={styles.logo}>
<Image alt='logo' height='100%' src={info.getValue().src} width='100%' />
<Image alt='logo' src={info.getValue().src} width={32} height={32} />
</div>
),
}),

View File

@ -1,6 +1,6 @@
import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import Tippy from '@tippyjs/react'
import classNames from 'classnames/bind'
import classNames from 'classnames'
import { AnimatedNumber, Apr, Button, CellAmount, SVG } from 'components/common'
import { convertPercentage } from 'functions'
import { formatValue } from 'libs/parse'
@ -32,7 +32,7 @@ export const useDepositColumns = () => {
header: '',
cell: (info) => (
<div className={styles.logo}>
<Image alt='logo' height='100%' src={info.getValue().src} width='100%' />
<Image alt='logo' src={info.getValue().src} width={32} height={32} />
</div>
),
}),

View File

@ -1,4 +1,4 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { TxBroadcastResult } from '@marsprotocol/wallet-connector'
import { useQueryClient } from '@tanstack/react-query'
import { Action, Notification, TxResponse } from 'components/common'
import { findByDenom } from 'functions'
@ -13,8 +13,7 @@ import { ltvWeightedDepositValue, maintainanceMarginWeightedDepositValue } from
import { lookup, lookupDecimals } from 'libs/parse'
import isEqual from 'lodash.isequal'
import { useRouter } from 'next/router'
import { useMemo, useState } from 'react'
import React from 'react'
import React, { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import { NotificationType, ViewType } from 'types/enums'
@ -54,7 +53,7 @@ export const RedbankAction = React.memo(
// ------------------
const [amount, setAmount] = useState(0)
const [submitted, setSubmitted] = useState(false)
const [response, setResponse] = useState<ExecuteResult>()
const [response, setResponse] = useState<TxBroadcastResult>()
const [error, setError] = useState<string>()
const [isMax, setIsMax] = useState<boolean>(false)
const [capHit, setCapHit] = useState<boolean>(false)
@ -126,7 +125,7 @@ export const RedbankAction = React.memo(
funds:
activeView === ViewType.Deposit || activeView === ViewType.Repay
? [{ denom, amount: amount > 0 ? amount.toFixed(0) : '1' }]
: [],
: undefined,
contract: redBankContractAddress,
})

View File

@ -1,4 +1,4 @@
import { ChainInfoID } from '@marsprotocol/wallet-connector'
import { ChainInfoID, WalletID } from '@marsprotocol/wallet-connector'
import atom from 'images/atom.svg'
import axlusdc from 'images/axlusdc.svg'
import juno from 'images/juno.svg'
@ -63,9 +63,9 @@ const OTHER_ASSETS: { [denom: string]: OtherAsset } = {
export const NETWORK_CONFIG: NetworkConfig = {
name: ChainInfoID.OsmosisTestnet,
hiveUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-hive/graphql',
rpcUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-rpc',
restUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-lcd',
hiveUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-hive/graphql/',
rpcUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-rpc/',
restUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-lcd/',
apolloAprUrl: 'https://stats.apollo.farm/api/apr/v1/all',
contracts: {
addressProvider: 'osmo17dyy6hyzzy6u5khy5lau7afa2y9kwknu0aprwqn8twndw2qhv8ls6msnjr',
@ -76,8 +76,8 @@ export const NETWORK_CONFIG: NetworkConfig = {
treasury: 'osmo1qv74pu0gjc9vuvkhayuj5j3q8fzmf4pnl643djqpv7enxr925g5q0wf7p3',
safetyFund: 'osmo1j2mnzs7eqld4umtwky4hyf6f7kqcsg7ragh2l76ev7ucxcjvdjrs3tdezf',
protocolRewardsCollector: 'osmo1xl7jguvkg807ya00s0l722nwcappfzyzrac3ug5tnjassnrmnfrs47wguz',
creditManager: 'osmo1prwnxn3vlvh0kqmwxn8whqnavk8ze9hrccwpsapysgpa3pj8r2csy84grp',
accountNft: 'osmo1ua5rw84jxg6e7ma4hx7v7yhqcks74cjnx38gpnsvtfzrtxhwcvjqgsxulx',
creditManager: 'osmo169xhpftsee275j3cjudj6qfzdpfp8sdllgeeprud4ynwr4sj6m4qel2ezp',
accountNft: 'osmo1xpgx06z2c6zjk49feq75swgv78m6dvht6wramu2gltzjz5j959nq4hggxz',
},
assets: {
base: ASSETS.osmo,
@ -85,7 +85,8 @@ export const NETWORK_CONFIG: NetworkConfig = {
other: [OTHER_ASSETS.mars, OTHER_ASSETS.axlusdc],
},
appUrl: 'https://testnet.osmosis.zone',
councilUrl: 'https://testnet.keplr.app/chains/mars-protocol-testnet',
councilUrl: 'https://testnet.keplr.app/chains/mars-hub-testnet',
wallets: [WalletID.Keplr, WalletID.Leap, WalletID.Cosmostation],
}
export const VAULT_CONFIGS: Vault[] = [
@ -104,7 +105,8 @@ export const VAULT_CONFIGS: Vault[] = [
color: '#DD5B65',
lockup: 86400,
provider: 'Apollo vault',
description: 'Up to 2× Leveraged Yield Farming with auto compounding of the LP tokens.',
description:
'Up to 2.67× leveraged yield farming with auto compounding of the OSMO-ATOM LP tokens.',
ltv: {
max: 0.625,
contract: 0.63,
@ -126,7 +128,8 @@ export const VAULT_CONFIGS: Vault[] = [
color: '#DD5B65',
lockup: 86400 * 14,
provider: 'Apollo vault',
description: 'Up to 2× Leveraged Yield Farming with auto compounding of the LP tokens.',
description:
'Up to 2.67× leveraged yield farming with auto compounding of the OSMO-ATOM LP tokens.',
ltv: {
max: 0.625,
contract: 0.63,
@ -148,7 +151,8 @@ export const VAULT_CONFIGS: Vault[] = [
color: '#DD5B65',
lockup: 86400 * 1,
provider: 'Apollo vault',
description: 'Up to 2× Leveraged Yield Farming with auto compounding of the LP tokens.',
description:
'Up to 1.67× leveraged yield farming with auto compounding of the OSMO-JUNO LP tokens.',
ltv: {
max: 0.4,
contract: 0.4115,
@ -170,7 +174,8 @@ export const VAULT_CONFIGS: Vault[] = [
color: '#DD5B65',
lockup: 86400 * 14,
provider: 'Apollo vault',
description: 'Up to 2× Leveraged Yield Farming with auto compounding of the LP tokens.',
description:
'Up to 1.67× leveraged yield farming with auto compounding of the OSMO-JUNO LP tokens.',
ltv: {
max: 0.4,
contract: 4.115,

View File

@ -1,4 +1,4 @@
import { ChainInfoID } from '@marsprotocol/wallet-connector'
import { ChainInfoID, WalletID } from '@marsprotocol/wallet-connector'
import atom from 'images/atom.svg'
import axlusdc from 'images/axlusdc.svg'
import mars from 'images/mars.svg'
@ -50,9 +50,9 @@ const OTHER_ASSETS: { [denom: string]: OtherAsset } = {
export const NETWORK_CONFIG: NetworkConfig = {
name: ChainInfoID.OsmosisTestnet,
hiveUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-hive/graphql',
rpcUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-rpc',
restUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-lcd',
hiveUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-hive/graphql/',
rpcUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-rpc/',
restUrl: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-lcd/',
apolloAprUrl: 'https://stats.apollo.farm/api/apr/v1/all',
contracts: {
addressProvider: 'osmo17dyy6hyzzy6u5khy5lau7afa2y9kwknu0aprwqn8twndw2qhv8ls6msnjr',
@ -72,7 +72,8 @@ export const NETWORK_CONFIG: NetworkConfig = {
other: [OTHER_ASSETS.mars],
},
appUrl: 'https://testnet.osmosis.zone',
councilUrl: 'https://testnet.keplr.app/chains/mars-protocol-testnet',
councilUrl: 'https://council.marsprotocol.io',
wallets: [WalletID.Keplr, WalletID.Leap, WalletID.Cosmostation, WalletID.Falcon],
}
export const VAULT_CONFIGS: Vault[] = [

View File

@ -11,7 +11,7 @@ export const DEFAULT_SLIPPAGE = 0.01
/* other */
export const FEE_EST_AMOUNT = '1'
export const SESSION_WALLET_KEY = 'walletConnection'
export const SESSION_WALLET_KEY = 'shuttle'
export const SWAP_THRESHOLD = 10
export const VAULT_DEPOSIT_BUFFER = 0.99
export const GAS_ADJUSTMENT = 1.3

View File

@ -19,7 +19,11 @@ export const DEFAULT_POSITION: Position = {
total: 0,
net: 0,
},
apy: 0,
apy: {
borrow: 5.2,
net: 7.7,
total: 19,
},
ltv: 0.5,
currentLeverage: 1,
}

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
export const getAmountFromUnlockRes = (unlockData: ExecuteResult) => {
const stringValue = unlockData?.logs[0].events
.find((item) => item.type === 'coin_received')
?.attributes.find((item) => item.key === 'amount')?.value
if (!stringValue) return ''
if (stringValue?.indexOf('ibc/') > 0) {
return stringValue.split('ibc/')[0]
}
return stringValue.split('factory/')[0]
}

View File

@ -1,4 +1,4 @@
import { findAssetByDenom, formatValue } from 'libs/parse'
import { findAssetByDenom, formatValue, lookup } from 'libs/parse'
import { Coin } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
export const getTokenValueFromCoins = (assets: Asset[], coins: Coin[] = []) => {
@ -7,6 +7,15 @@ export const getTokenValueFromCoins = (assets: Asset[], coins: Coin[] = []) => {
if (!asset) return ''
return `${formatValue(Number(token.amount) / 10 ** asset.decimals, 2, 2, true)} ${asset.symbol}`
const convertedValue = lookup(Number(token.amount), asset.symbol, asset.decimals)
return formatValue(
convertedValue,
2,
2,
true,
convertedValue >= 0.01 ? false : '>',
` ${asset.symbol}`,
)
})
}

View File

@ -1,6 +1,5 @@
// @index(['./*.ts'], f => `export { ${f.name} } from '${f.path}'`)
export { coinsToActionCoins } from './coinsToActionCoins'
export { getAmountFromUnlockRes } from './getAmountFromUnlockRes'
export { getAmountsFromActiveVault } from './getAmountsFromActiveVault'
export { getClosePositionActions } from './getClosePositionActions'
export { getCoinFromPosition } from './getCoinFromPosition'

View File

@ -0,0 +1,15 @@
import { Coin } from '@cosmjs/launchpad'
import { TxBroadcastResult } from '@marsprotocol/wallet-connector'
import { extractCoinFromLog } from 'libs/parse'
export const getFeeFromResponse = (response: TxBroadcastResult): Coin | null => {
const stringValue = response?.response.events
.filter((msg: Record<string, string>) => msg.type === 'tx')
.map((msg: Record<string, string>) => msg.attributes)
.flat()
.find((msg: Record<string, string>) => msg.key === 'fee')?.value
if (!stringValue) return null
return extractCoinFromLog(stringValue)
}

View File

@ -2,6 +2,7 @@
export { convertPercentage } from './convertPercentage'
export { findByDenom } from './findByDenom'
export { formatToValueSymbol } from './formatToValueSymbol'
export { getFeeFromResponse } from './getFeeFromResponse'
export { getSwapUrl } from './getSwapUrl'
export { updateExchangeRate } from './updateExchangeRate'
// @endindex

View File

@ -1,5 +1,5 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { getAmountFromUnlockRes, getTokenValueFromCoins } from 'functions/fields'
import { TxBroadcastResult } from '@marsprotocol/wallet-connector'
import { getTokenValueFromCoins } from 'functions/fields'
import { useUnlockMessages } from 'hooks/queries'
import { extractCoinFromLog, parseActionMessages } from 'libs/parse'
import { useCallback, useEffect, useState } from 'react'
@ -8,7 +8,7 @@ import useStore from 'store'
import { Coin } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
export const useFieldsActionMessages = (
data?: ExecuteResult,
data?: TxBroadcastResult,
vault?: ActiveVault,
): {
depositMessage?: FieldsAction
@ -31,11 +31,9 @@ export const useFieldsActionMessages = (
const getDepositedCoins = useCallback((messages: unknown[]): Coin[] => {
try {
return (
messages.filter(
(message: any) => message?.action === 'rover/execute/update_credit_account',
) as any
).map((msg: any) => extractCoinFromLog(msg.coin_deposited))
return (messages.filter((message: any) => message?.action === 'callback/deposit') as any).map(
(msg: any) => extractCoinFromLog(msg.coin_deposited),
)
} catch {
return []
}
@ -44,7 +42,7 @@ export const useFieldsActionMessages = (
const getBorrowedCoins = useCallback((messages: any[]): Coin[] => {
try {
return (
messages.filter((message: any) => message?.action === 'outposts/red-bank/borrow') as any
messages.filter((message: any) => message?.action === 'borrow' && message?.denom) as any
).map((msg: any) => ({ denom: msg.denom, amount: msg.amount }))
} catch {
return []
@ -53,9 +51,9 @@ export const useFieldsActionMessages = (
const getSwapCoinIn = useCallback((messages: unknown[]): Coin | null => {
try {
return (
messages.filter((message: any) => message?.action === 'rover/swapper/swap_fn') as any
).map((msg: any) => ({ denom: msg.denom_in, amount: msg.amount_in }))[0]
return (messages.filter((message: any) => message?.action === 'swap_fn') as any).map(
(msg: any) => ({ denom: msg.denom_in, amount: msg.amount_in }),
)[0]
} catch {
return null
}
@ -64,7 +62,7 @@ export const useFieldsActionMessages = (
const getSwapCoinOut = useCallback((messages: unknown[]): Coin | null => {
try {
const coinsSwapOutIndex = messages.findIndex(
(message: any) => message?.action === 'rover/swapper/transfer_result',
(message: any) => message?.action === 'transfer_result',
) as any
const msg: any = messages[coinsSwapOutIndex + 1]
@ -80,8 +78,10 @@ export const useFieldsActionMessages = (
const getRepayCoin = useCallback((messages: any[]): Coin | null => {
try {
return messages
.filter((message: any) => message?.action === ('outposts/red-bank/repay' as any))
.map((msg: any) => ({ denom: msg.denom, amount: msg.amount }))[0]
.filter((message: any) => message?.action === ('repay' as any) && message?.denom)
.map((msg: any) => {
return { denom: msg.denom, amount: msg.amount }
})[0]
} catch {
return null
}
@ -90,9 +90,7 @@ export const useFieldsActionMessages = (
const getWithdrawnCoins = useCallback((messages: any[]): Coin[] => {
try {
return (
messages.filter(
(message: any) => message?.action === 'rover/credit-manager/callback/withdraw',
) as any
messages.filter((message: any) => message?.action === 'callback/withdraw') as any
).map((msg: any) => {
return extractCoinFromLog(msg.coin_withdrawn)
})
@ -101,6 +99,18 @@ export const useFieldsActionMessages = (
}
}, [])
const getVaultUnlockAmount = useCallback((messages: any[]): string | null => {
try {
return (
messages.filter((message: any) => message?.action === 'vault/request_unlock') as any
).map((msg: any) => {
return msg.unlock_amount
})[0]
} catch {
return null
}
}, [])
useEffect(() => {
if (!data) {
return
@ -117,9 +127,10 @@ export const useFieldsActionMessages = (
const swapCoinOut = getSwapCoinOut(messages)
const repayCoin = getRepayCoin(messages)
const withdrawnCoins = getWithdrawnCoins(messages)
const vaultUnlockAmount = getVaultUnlockAmount(messages)
if (!vaultTokenAmount) {
setVaultTokenAmount(getAmountFromUnlockRes(data))
if (!vaultTokenAmount && vaultUnlockAmount) {
setVaultTokenAmount(vaultUnlockAmount)
}
if (depositedCoins.length) {
@ -170,6 +181,7 @@ export const useFieldsActionMessages = (
getRepayCoin,
getWithdrawnCoins,
vaultTokenAmount,
getVaultUnlockAmount,
whitelistedAssets,
])

View File

@ -1,12 +1,32 @@
import { StdFee } from '@cosmjs/stargate'
import { useMutation } from '@tanstack/react-query'
import { parseActionMessages } from 'libs/parse'
import useStore from 'store'
export const useCreateCreditAccount = () => {
const creditManagerClient = useStore((s) => s.creditManagerClient)
const executeMsg = useStore((s) => s.executeMsg)
const networkConfig = useStore((s) => s.networkConfig)
return useMutation(async (fee: StdFee) => {
const executeResult = await creditManagerClient?.createCreditAccount(fee)
return executeResult?.logs[0].events[2].attributes[6].value
const message = { create_credit_account: {} }
if (!networkConfig) return null
return executeMsg({
msg: message,
fee,
contract: networkConfig.contracts.creditManager,
}).then((broadcastResult) => {
if (broadcastResult) {
try {
const messages = parseActionMessages(broadcastResult)
return (
messages?.find(
(message: Record<string, string>) => message?.action === 'mint',
) as Record<string, string>
)['token_id']
} catch {}
}
})
})
}

View File

@ -13,29 +13,33 @@ interface Props {
export const useUpdateAccount = () => {
const queryClient = useQueryClient()
const creditManagerClient = useStore((s) => s.creditManagerClient)
const executeMsg = useStore((s) => s.executeMsg)
const networkConfig = useStore((s) => s.networkConfig)
const getVaults = useStore((s) => s.getVaults)
return useMutation(
async (props: Props) => {
queryClient.removeQueries([QUERY_KEYS.ESTIMATE_FARM_FEE])
return creditManagerClient?.updateCreditAccount(
{
accountId: props.accountId,
actions: props.actions,
},
props.fee,
undefined,
props.funds,
)
},
{
onSuccess: () => {
return useMutation(async (props: Props) => {
queryClient.removeQueries([QUERY_KEYS.ESTIMATE_FARM_FEE])
const message = {
update_credit_account: {
account_id: props.accountId,
actions: props.actions,
},
}
if (!networkConfig) return
return executeMsg({
msg: message,
fee: props.fee,
contract: networkConfig.contracts.creditManager,
funds: props.funds,
}).then((broadcastResult) => {
if (broadcastResult?.response.code === 0) {
getVaults({ refetch: true })
},
onError: (error: Error) => {
return `${error.message}`
},
},
)
return { result: broadcastResult }
} else {
return { error: broadcastResult?.rawLogs }
}
})
})
}

View File

@ -16,4 +16,5 @@ export { useUnlockMessages } from './useUnlockMessages'
export { useUserBalance } from './useUserBalance'
export { useUserDebt } from './useUserDebt'
export { useUserDeposit } from './useUserDeposit'
export { useUserIcns } from './useUserIcns'
// @endindex

View File

@ -9,7 +9,7 @@ import { Action, Coin } from 'types/generated/mars-credit-manager/MarsCreditMana
import { useEstimateFarmFee } from './useEstimateFarmFee'
interface Props {
accountId?: string
accountId?: null | string
prevPosition?: Position
position: Position
vault: Vault

View File

@ -1,14 +1,13 @@
import { MsgExecuteContractEncodeObject } from '@cosmjs/cosmwasm-stargate'
import { GasPrice } from '@cosmjs/stargate'
import { MsgExecuteContract } from '@marsprotocol/wallet-connector'
import { useQuery } from '@tanstack/react-query'
import BigNumber from 'bignumber.js'
import { GAS_ADJUSTMENT, GAS_PRICE } from 'constants/appConstants'
import { GAS_ADJUSTMENT } from 'constants/appConstants'
import useStore from 'store'
import { QUERY_KEYS } from 'types/enums/queryKeys'
import { Action, Coin } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
interface Props {
accountId?: string
accountId?: null | string
actions?: Action[]
funds?: Coin[]
isCreate?: boolean
@ -17,44 +16,48 @@ interface Props {
export const useEstimateFarmFee = (props: Props) => {
const userWalletAddress = useStore((s) => s.userWalletAddress)
const creditManagerMsgComposer = useStore((s) => s.creditManagerMsgComposer)
const client = useStore((s) => s.client)
const networkConfig = useStore((s) => s.networkConfig)
return useQuery(
[QUERY_KEYS.ESTIMATE_FARM_FEE, props.actions],
async () => {
const gasPrice = GasPrice.fromString(GAS_PRICE)
const gasAdjustment = GAS_ADJUSTMENT
if (!creditManagerMsgComposer || !client) return null
if (!client) return null
let msg: MsgExecuteContractEncodeObject | null = null
if (props.isCreate) {
msg = creditManagerMsgComposer.createCreditAccount()
} else if (props.accountId && props.actions?.length) {
msg = creditManagerMsgComposer.updateCreditAccount(
{
accountId: props.accountId,
actions: props.actions,
},
props.funds,
)
}
if (!msg) return null
if (!networkConfig) return null
try {
const gasUsed = await client.simulate(userWalletAddress, [msg], undefined)
const fee = new BigNumber(Number(gasPrice.amount))
.multipliedBy(gasUsed)
.multipliedBy(gasAdjustment)
return {
amount: [{ denom: 'uosmo', amount: fee.toFixed(0) }],
gas: new BigNumber(gasUsed).multipliedBy(gasAdjustment).toFixed(0),
const simulateOptions = {
messages: [
new MsgExecuteContract({
sender: userWalletAddress,
contract: networkConfig.contracts.creditManager,
msg: props.isCreate
? { create_credit_account: {} }
: {
update_credit_account: {
account_id: props.accountId,
actions: props.actions,
},
},
funds: props.funds,
}),
],
wallet: client.recentWallet,
}
const result = await client.simulate(simulateOptions)
return result.success
? {
amount: result.fee ? result.fee.amount : [],
gas: new BigNumber(result.fee ? result.fee.gas : 0)
.multipliedBy(gasAdjustment)
.toFixed(0),
}
: null
} catch {
return null
}
@ -63,7 +66,6 @@ export const useEstimateFarmFee = (props: Props) => {
enabled:
!props.isLoading &&
!!client &&
!!creditManagerMsgComposer &&
!!userWalletAddress &&
(props.isCreate || (!!props.accountId && !!props.actions?.length)),
},

View File

@ -1,10 +1,9 @@
import { MsgExecuteContractEncodeObject } from '@cosmjs/cosmwasm-stargate'
import { toUtf8 } from '@cosmjs/encoding'
import { Coin } from '@cosmjs/proto-signing'
import { GasPrice } from '@cosmjs/stargate'
import { MsgExecuteContract } from '@marsprotocol/wallet-connector'
import { useQuery } from '@tanstack/react-query'
import BigNumber from 'bignumber.js'
import { GAS_ADJUSTMENT, GAS_PRICE } from 'constants/appConstants'
import { GAS_ADJUSTMENT } from 'constants/appConstants'
import useStore from 'store'
import { QUERY_KEYS } from 'types/enums/queryKeys'
import { ContractMsg } from 'types/types'
@ -22,37 +21,35 @@ export const useEstimateFee = (props: Props) => {
const client = useStore((s) => s.client)
return useQuery(
[QUERY_KEYS.ESTIMATE_FEE],
[QUERY_KEYS.ESTIMATE_FEE, props.msg],
async () => {
const sender = props.sender ? props.sender : userWalletAddress
const gasPrice = GasPrice.fromString(GAS_PRICE)
const gasAdjustment = GAS_ADJUSTMENT
if (!client) return
const msg = props.executeMsg
? props.executeMsg
: {
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
value: {
sender: sender,
contract: props.contract,
msg: toUtf8(JSON.stringify(props.msg)),
funds: props.funds,
},
}
if (!client || !props.contract || !props.msg) return
try {
const gasUsed = await client.simulate(sender, [msg], undefined)
const fee = new BigNumber(Number(gasPrice.amount))
.multipliedBy(gasUsed)
.multipliedBy(gasAdjustment)
return {
amount: [{ denom: 'uosmo', amount: fee.toFixed(0) }],
gas: new BigNumber(gasUsed).multipliedBy(gasAdjustment).toFixed(0),
const simulateOptions = {
messages: [
new MsgExecuteContract({
sender: sender,
contract: props.contract,
msg: props.msg,
funds: props.funds,
}),
],
wallet: client.recentWallet,
}
const result = await client.simulate(simulateOptions)
return result.success
? {
amount: result.fee ? result.fee.amount : [],
gas: new BigNumber(result.fee ? result.fee.gas : 0)
.multipliedBy(gasAdjustment)
.toFixed(0),
}
: null
} catch {
return null
}

View File

@ -11,13 +11,15 @@ interface Props {
export const useProvideLiquidity = (props: Props) => {
const creditManagerClient = useStore((s) => s.creditManagerClient)
return useQuery(
return useQuery<number>(
[QUERY_KEYS.PROVIDE_LIQUIDITY, props.coins],
async () => {
if (!creditManagerClient || !props.coins.length) return null
return creditManagerClient.estimateProvideLiquidity({
coinsIn: props.coins,
lpTokenOut: props.vault?.denoms.lpToken || '',
return creditManagerClient.query({
estimate_provide_liquidity: {
coins_in: props.coins,
lp_token_out: props.vault?.denoms.lpToken || '',
},
})
},
{

View File

@ -5,7 +5,7 @@ import { useAsset } from 'hooks/data'
import useStore from 'store'
import { QUERY_KEYS } from 'types/enums/queryKeys'
const poolsEndpoint = '/osmosis/gamm/v1beta1/pools/'
const poolsEndpoint = 'osmosis/gamm/v1beta1/pools/'
export const useSpotPrice = (symbol: string) => {
const displayCurrency = useStore((s) => s.displayCurrency)

View File

@ -3,8 +3,8 @@ import { getTokenValueFromCoins } from 'functions/fields'
import { formatUnlockDate } from 'libs/parse'
import { useTranslation } from 'react-i18next'
import useStore from 'store'
import { VaultClient } from 'types/classes'
import { QUERY_KEYS } from 'types/enums/queryKeys'
import { MarsMockVaultClient } from 'types/generated/mars-mock-vault/MarsMockVault.client'
interface Props {
vault?: ActiveVault
@ -23,15 +23,19 @@ export const useUnlockMessages = (props: Props) => {
async () => {
if (!client || !userWalletAddress || !props.vault || !creditManagerClient) return null
const vaultClient = new MarsMockVaultClient(client, userWalletAddress, props.vault.address)
const lpTokenAmount = await vaultClient.previewRedeem({
amount: props.vaultTokenAmount,
const vaultClient = new VaultClient(props.vault.address, client)
const lpTokenAmount = await vaultClient.query({
preview_redeem: {
amount: props.vaultTokenAmount,
},
})
const lpToken = { denom: props.vault.denoms.lpToken, amount: lpTokenAmount }
const coins = await creditManagerClient.estimateWithdrawLiquidity({
lpToken,
const coins = await creditManagerClient.query({
estimate_withdraw_liquidity: {
lp_token: lpToken,
},
})
return [

View File

@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'
import { gql, request } from 'graphql-request'
import { useMemo } from 'react'
import useStore from 'store'
import { QUERY_KEYS } from 'types/enums/queryKeys'
export interface UserBalanceData {
balance: {
@ -16,7 +17,7 @@ export const useUserBalance = () => {
const processUserBalanceQuery = useStore((s) => s.processUserBalanceQuery)
const result = useQuery<UserBalanceData>(
[],
[QUERY_KEYS.USER_BALANCE],
async () => {
return await request(
hiveUrl!,

View File

@ -0,0 +1,54 @@
import { useQuery } from '@tanstack/react-query'
import { gql, request } from 'graphql-request'
import { useMemo } from 'react'
import useStore from 'store'
import { QUERY_KEYS } from 'types/enums/queryKeys'
export interface UserIcnsData {
wasm: {
contractQuery: {
names: string[]
primary_name: string
}
}
}
export const useUserIcns = () => {
/* only possible to query on mainnet */
const hiveUrl = 'https://osmosis-mars-frontend.simply-vc.com.mt/GGSFGSFGFG34/osmosis-hive/graphql'
const resolverContract = 'osmo1xk0s8xgktn9x5vwcgtjdxqzadg88fgn33p8u9cnpdxwemvxscvast52cdd'
const userWalletAddress = useStore((s) => s.userWalletAddress)
const setUserIcns = useStore((s) => s.setUserIcns)
const result = useQuery<UserIcnsData>(
[QUERY_KEYS.USER_ICNS],
async () => {
return await request(
hiveUrl!,
gql`
query UserIcnsQuery {
wasm {
contractQuery(contractAddress:"${resolverContract}", query: {
icns_names:{
address: "${userWalletAddress}"
}
})
}
}`,
)
},
{
enabled: !!userWalletAddress || false,
onSuccess: (data) => {
const icns = data.wasm.contractQuery.primary_name
if (icns !== '') setUserIcns(icns)
},
},
)
return {
...result,
data: useMemo(() => result.data, [result.data]),
}
}

View File

@ -3,6 +3,12 @@ import LanguageDetector from 'i18next-browser-languagedetector'
import HttpApi from 'i18next-http-backend'
import { initReactI18next } from 'react-i18next'
declare module 'i18next' {
interface CustomTypeOptions {
returnNull: false
}
}
i18next
.use(HttpApi)
.use(LanguageDetector)
@ -11,7 +17,7 @@ i18next
backend: {
crossDomain: true,
loadPath() {
return 'https://raw.githubusercontent.com/mars-protocol/translations/develop/{{lng}}.json'
return 'https://raw.githubusercontent.com/mars-protocol/translations/main/{{lng}}.json'
},
},
react: {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,4 +1,4 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { TxBroadcastResult } from '@marsprotocol/wallet-connector'
import BigNumber from 'bignumber.js'
import { DAY_IN_SECONDS, HOUR_IN_SECONDS, MINUTE_IN_SECONDS } from 'constants/timeConstants'
import moment from 'moment'
@ -282,21 +282,31 @@ export const extractCoinFromLog = (text: string) => {
return { amount: arr[0], denom: arr[1] }
}
export const parseActionMessages = (data: ExecuteResult) => {
const wasmEvents = data.logs[0].events.find((object) => object.type === 'wasm')
if (wasmEvents) {
return wasmEvents.attributes.reduce((prev: {}[], curr) => {
if (curr.key === '_contract_address') {
export const parseActionMessages = (data: TxBroadcastResult) => {
const wasmEvents: [] = data.response.events
.filter((object: Record<string, string>) => object.type === 'wasm')
.map((event: Record<string, string>) => event?.attributes)
.flat()
if (wasmEvents.length) {
return wasmEvents.reduce((prev: {}[], curr: any) => {
if (curr.key === 'action') {
prev.push({ [curr.key]: curr.value })
return prev
} else {
Object.assign(prev[prev.length - 1], { [curr.key]: curr.value })
if (prev.length) {
Object.assign(prev[prev.length - 1], { [curr.key]: curr.value })
}
return prev
}
}, [])
}
}
export const toBase64 = (obj: object) => {
return Buffer.from(JSON.stringify(obj)).toString('base64')
}
export const ltvToLeverage = (ltv: number) => {
const leverage = 1 / (1 - ltv)
return new BigNumber(leverage).decimalPlaces(2).toNumber()

View File

@ -19,7 +19,11 @@ export const position: Position = {
total: 0,
net: 0,
},
apy: 0,
apy: {
borrow: 5.2,
net: 7.7,
total: 19,
},
ltv: 0.5,
currentLeverage: 1,
}

View File

@ -7,7 +7,7 @@ import {
} from 'components/fields'
import { FIELDS_FEATURE } from 'constants/appConstants'
import { useRouter } from 'next/router'
import React, { useEffect } from 'react'
import { useEffect } from 'react'
import useStore from 'store'
import styles from './Fields.module.scss'
@ -23,7 +23,8 @@ const Fields = () => {
const userWalletAddress = useStore((s) => s.userWalletAddress)
useEffect(() => {
if (!getVaults || !accountNftClient || !client || !creditManagerClient) return
if (!getVaults || !accountNftClient || !client || !creditManagerClient || !userWalletAddress)
return
if (userWalletAddress && prefUserWalletAddress !== userWalletAddress) {
prefUserWalletAddress = userWalletAddress
getVaults({ refetch: true })

View File

@ -25,7 +25,9 @@ const CloseVaultPosition = () => {
}
useEffect(() => {
if (!closeFee || !closeActions || !activeVault || isLoading || data || error) return
if (!closeFee || !closeActions || !activeVault || isLoading || data?.error || data?.result)
return
mutate({
accountId: activeVault.position.accountId,
actions: closeActions,
@ -46,7 +48,6 @@ const CloseVaultPosition = () => {
return (
<ClosePositionResponse
data={data}
error={error}
isLoading={isLoading}
accountId={ref.current.position.accountId}
/>

View File

@ -4,7 +4,6 @@ import { useCreateCreditAccount, useUpdateAccount } from 'hooks/mutations'
import { useEditPosition, useEstimateFarmFee } from 'hooks/queries'
import { getTimeAndUnit } from 'libs/parse'
import router from 'next/router'
import React from 'react'
import { useTranslation } from 'react-i18next'
import styles from './SetupPosition.module.scss'
@ -22,7 +21,6 @@ const SetupPosition = (props: Props) => {
data: accountId,
isLoading: isLoadingCreate,
} = useCreateCreditAccount()
const {
mutate: enterVault,
data: enterVaultData,
@ -57,11 +55,10 @@ const SetupPosition = (props: Props) => {
enterVault({ accountId, actions: editActions, fee: editFee, funds: editFunds })
}
if (isLoadingEnterVault || enterVaultError || enterVaultData) {
if (isLoadingEnterVault || enterVaultData) {
return (
<SetUpResponse
data={enterVaultData}
error={enterVaultError}
isLoading={isLoadingEnterVault}
accountId={accountId || ''}
/>

View File

@ -162,31 +162,18 @@ const EditVault = (props: Props) => {
if (isLoadingEdit || editData || editError) {
return (
<EditResponse
data={editData}
error={editError}
isLoading={isLoadingEdit}
activeVault={props.activeVault}
/>
<EditResponse data={editData} isLoading={isLoadingEdit} activeVault={props.activeVault} />
)
}
if (isLoadingRepay || repayData || repayError) {
return (
<RepayResponse
data={repayData}
error={repayError}
isLoading={isLoadingRepay}
vault={props.activeVault}
/>
)
return <RepayResponse data={repayData} isLoading={isLoadingRepay} vault={props.activeVault} />
}
if (isLoadingUnlock || unlockData || unlockError) {
return (
<UnlockResponse
data={unlockData}
error={unlockError}
isLoading={isLoadingUnlock}
activeVault={props.activeVault}
/>

View File

@ -49,14 +49,7 @@ const RepayVault = (props: Props) => {
}
if (isLoadingRepay || repayData || repayError) {
return (
<RepayResponse
data={repayData}
error={repayError}
isLoading={isLoadingRepay}
vault={props.activeVault}
/>
)
return <RepayResponse data={repayData} isLoading={isLoadingRepay} vault={props.activeVault} />
}
const isSameAmounts = isEqual(prevPosition.amounts, repayPosition.amounts)

View File

@ -5,7 +5,6 @@ import { useActiveVault } from 'hooks/data'
import { useUpdateAccount } from 'hooks/mutations'
import { useRequestUnlockPosition } from 'hooks/queries/useRequestUnlockPosition'
import { useRouter } from 'next/router'
import React from 'react'
import { useTranslation } from 'react-i18next'
import styles from './UnlockDisclaimer.module.scss'
@ -49,12 +48,7 @@ const Unlock = () => {
if (unlockData || unlockError || isLoadingUnlock) {
return (
<UnlockResponse
data={unlockData}
error={unlockError}
isLoading={isLoadingUnlock}
activeVault={activeVault}
/>
<UnlockResponse data={unlockData} isLoading={isLoadingUnlock} activeVault={activeVault} />
)
}

View File

@ -1,11 +1,15 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { LcdClient } from '@cosmjs/launchpad'
import { Coin, StdFee } from '@cosmjs/stargate'
import { WalletChainInfo, WalletSigningCosmWasmClient } from '@marsprotocol/wallet-connector'
import {
SimplifiedChainInfo,
TxBroadcastResult,
WalletClient,
} from '@marsprotocol/wallet-connector'
import { BlockHeightData } from 'hooks/queries/useBlockHeight'
import { MarketDepositsData } from 'hooks/queries/useMarketDeposits'
import { SafetyFundBalanceData } from 'hooks/queries/useSafetyFundBalance'
import { UserBalanceData } from 'hooks/queries/useUserBalance'
import { UserIcnsData } from 'hooks/queries/useUserIcns'
import { Network } from 'types/enums/network'
import { ContractMsg } from 'types/types'
@ -22,8 +26,8 @@ export interface CommonSlice {
decimals: number
}
baseToDisplayCurrencyRatio?: number
chainInfo?: WalletChainInfo
client?: WalletSigningCosmWasmClient
chainInfo?: SimplifiedChainInfo
client?: WalletClient
currentNetwork: Network
displayCurrency: {
denom: string
@ -51,7 +55,7 @@ export interface CommonSlice {
userUnclaimedRewards: string
userMarsTokenBalances: Coin[]
userWalletAddress: string
userWalletName: string
userIcns?: string
vaultConfigs: Vault[]
whitelistedAssets: Asset[]
// ------------------
@ -65,7 +69,7 @@ export interface CommonSlice {
contract: string
fee: StdFee
sender?: string
}) => Promise<ExecuteResult | undefined>
}) => Promise<TxBroadcastResult | undefined>
loadNetworkConfig: () => void
queryContract: <T>(
contractAddress: string,
@ -76,22 +80,23 @@ export interface CommonSlice {
// ------------------
// SETTERS
// ------------------
setChainInfo: (chainInfo: WalletChainInfo) => void
setChainInfo: (chainInfo: SimplifiedChainInfo) => void
setCurrentNetwork: (network: Network) => void
setTutorialStep: (type: 'fields' | 'redbank', step?: number) => void
setLcdClient: (rpc: string, chainID: string) => void
setNetworkError: (isError: boolean) => void
setClient: (client: WalletSigningCosmWasmClient) => void
setClient: (client: WalletClient) => void
setQueryError: (name: string, isError: boolean) => void
setServerError: (isError: boolean) => void
setUserIcns: (icns: string) => void
setUserWalletAddress: (address: string) => void
setUserWalletName: (name: string) => void
// ------------------
// QUERY RELATED
// ------------------
previousBlockHeightQueryData?: BlockHeightData
previousSafetyFundBalanceQueryData?: SafetyFundBalanceData
previousUserBalanceQueryData?: UserBalanceData
previousUserIcnsQueryData?: UserIcnsData
previousUserUnclaimedBalanceQueryData?: number
processMarketDepositsQuery: (data: MarketDepositsData) => void
processUserBalanceQuery: (data: UserBalanceData) => void

View File

@ -1,16 +1,12 @@
import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { MarsAccountNftInterface } from 'types/generated/mars-account-nft/MarsAccountNft.client'
import { MarsCreditManagerClient } from 'types/generated/mars-credit-manager/MarsCreditManager.client'
import { AccountNftClient, CreditManagerClient } from 'types/classes'
import { MarsCreditManagerMessageComposer } from 'types/generated/mars-credit-manager/MarsCreditManager.message-composer'
export interface FieldsSlice {
accountNftClient?: MarsAccountNftInterface
creditManagerClient?: MarsCreditManagerClient
accountNftClient?: AccountNftClient
creditManagerClient?: CreditManagerClient
creditManagerMsgComposer?: MarsCreditManagerMessageComposer
isRepay: boolean
position?: Position
setAccountNftClient: (client: SigningCosmWasmClient) => void
setCreditManagerClient: (client: SigningCosmWasmClient) => void
setCreditManagerMsgComposer: (address: string, contract: string) => void
setIsRepay: (isRepay: boolean) => void
setPosition: (position?: Position) => void

View File

@ -1,7 +1,11 @@
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { LcdClient } from '@cosmjs/launchpad'
import { Coin } from '@cosmjs/stargate'
import { WalletChainInfo, WalletSigningCosmWasmClient } from '@marsprotocol/wallet-connector'
import {
MsgExecuteContract,
SimplifiedChainInfo,
TxBroadcastResult,
WalletClient,
} from '@marsprotocol/wallet-connector'
import BigNumber from 'bignumber.js'
import { BlockHeightData } from 'hooks/queries/useBlockHeight'
import { MarketDepositsData } from 'hooks/queries/useMarketDeposits'
@ -46,7 +50,6 @@ const commonSlice = (
queryErrors: [],
slippage: 0.02,
tutorialSteps: { redbank: 1, fields: 1 },
userWalletName: '',
userBalances: [],
userMarsTokenBalances: [],
userUnclaimedRewards: '0',
@ -80,20 +83,30 @@ const commonSlice = (
return new BigNumber(coin.amount).div(exchangeRate).toNumber()
},
executeMsg: async (options: StrategyExecuteMsgOptions): Promise<ExecuteResult | undefined> => {
executeMsg: async (
options: StrategyExecuteMsgOptions,
): Promise<TxBroadcastResult | undefined> => {
const client = get().client!
if (!options.sender) options.sender = get().userWalletAddress
const broadcastOptions = {
messages: [
new MsgExecuteContract({
sender: options.sender,
contract: options.contract,
msg: options.msg,
funds: options.funds,
}),
],
feeAmount: options.fee.amount[0].amount,
gasLimit: options.fee.gas,
memo: undefined,
wallet: client.recentWallet,
}
try {
return client.execute(
options.sender,
options.contract,
options.msg as Record<string, unknown>,
options.fee,
undefined,
options.funds,
)
return client.broadcast(broadcastOptions)
} catch (e) {}
},
loadNetworkConfig: async () => {
@ -133,7 +146,7 @@ const commonSlice = (
lcdClient: new LcdClient(rpc),
})
},
setChainInfo: (chainInfo: WalletChainInfo) => set({ chainInfo }),
setChainInfo: (chainInfo: SimplifiedChainInfo) => set({ chainInfo }),
setCurrentNetwork: (network: Network) => set({ currentNetwork: network }),
setNetworkError: (isError: boolean) => {
const errors = get().errors
@ -142,7 +155,7 @@ const commonSlice = (
set({ errors })
}
},
setClient: (client: WalletSigningCosmWasmClient) => set({ client }),
setClient: (client: WalletClient) => set({ client }),
setQueryError: (name: string, isError: boolean) => {
const errors = get().errors
const queryErrors = get().queryErrors
@ -171,7 +184,7 @@ const commonSlice = (
set({ tutorialSteps })
},
setUserWalletAddress: (address: string) => set({ userWalletAddress: address }),
setUserWalletName: (name: string) => set({ userWalletName: name }),
setUserIcns: (icns: string) => set({ userIcns: icns }),
// -------------------
// QUERY RELATED
// -------------------

View File

@ -1,38 +1,11 @@
import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { FieldsSlice } from 'store/interfaces/fields.interface'
import { Store } from 'store/interfaces/store.interface'
import { MarsAccountNftClient } from 'types/generated/mars-account-nft/MarsAccountNft.client'
import { MarsCreditManagerClient } from 'types/generated/mars-credit-manager/MarsCreditManager.client'
import { MarsCreditManagerMessageComposer } from 'types/generated/mars-credit-manager/MarsCreditManager.message-composer'
import { GetState } from 'zustand'
import { NamedSet } from 'zustand/middleware'
const fieldsSlice = (set: NamedSet<Store>, get: GetState<Store>): FieldsSlice => ({
isRepay: false,
setAccountNftClient: (client: SigningCosmWasmClient) => {
const contracts = get().networkConfig?.contracts
const userWalletAddress = get().userWalletAddress
if (contracts?.accountNft) {
set({
accountNftClient: new MarsAccountNftClient(client, userWalletAddress, contracts.accountNft),
})
}
},
setCreditManagerClient: (client: SigningCosmWasmClient) => {
const contracts = get().networkConfig?.contracts
const userWalletAddress = get().userWalletAddress
if (contracts?.accountNft && userWalletAddress) {
set({
creditManagerClient: new MarsCreditManagerClient(
client,
userWalletAddress,
contracts.creditManager,
),
})
}
},
setCreditManagerMsgComposer: (sender: string, contract: string) => {
set({ creditManagerMsgComposer: new MarsCreditManagerMessageComposer(sender, contract) })
},

View File

@ -90,7 +90,7 @@ const oraclesSlice = (set: NamedSet<Store>, get: GetState<Store>): OraclesSlice
const denom = asset.denom
if (denom === get().baseCurrency.denom) {
updateExchangeRate({ denom, amount: '1' }, exchangeRates)
exchangeRates.push({ denom, amount: '1' })
return
}

View File

@ -5,12 +5,13 @@ import { convertAprToApy, leverageToLtv } from 'libs/parse'
import moment from 'moment'
import { Store } from 'store/interfaces/store.interface'
import { Options, VaultsSlice } from 'store/interfaces/vaults.interface.'
import { VaultClient } from 'types/classes'
import {
ArrayOfVaultInfoResponse,
LockingVaultAmount,
Positions,
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { VaultBaseForString } from 'types/generated/mars-mock-credit-manager/MarsMockCreditManager.types'
import { MarsMockVaultClient } from 'types/generated/mars-mock-vault/MarsMockVault.client'
import { GetState } from 'zustand'
import { NamedSet } from 'zustand/middleware'
@ -25,12 +26,14 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
const nftClient = get().accountNftClient!
const address = get().userWalletAddress
const accountIds = await nftClient
.tokens({ owner: address, limit: 100 })
.then((result) => result.tokens)
const accountIds: string[] = await nftClient
.query({ tokens: { owner: address, limit: 100 } })
.then((result: { tokens: string[] }) => result.tokens)
const creditManagerClient = get().creditManagerClient
const promises = accountIds?.map((id) => creditManagerClient?.positions({ accountId: id }))
const promises = accountIds?.map((id) =>
creditManagerClient?.query({ positions: { account_id: id } }),
)
const newCreditAccounts = await Promise.all(promises).then((result) =>
result.map((value) => value as Positions).filter((positions) => positions.vaults.length),
@ -58,10 +61,12 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
.toString()
return {
coins: await creditManagerClient.estimateWithdrawLiquidity({
lpToken: {
amount: amount,
denom: lpToken.denom,
coins: await creditManagerClient.query({
estimate_withdraw_liquidity: {
lp_token: {
amount: amount,
denom: lpToken.denom,
},
},
}),
vaultAddress: lpToken.vaultAddress,
@ -91,11 +96,14 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
?.unlocking[0]?.id
if (!client || !vault || isNaN(lockupId)) return null
const vaultClient = new VaultClient(vault.address, client)
return {
unlockAtTimestamp: Math.round(
Number(
(
await client.queryContractSmart(vault.address, {
await vaultClient.query({
vault_extension: { lockup: { unlocking_position: { lockup_id: lockupId } } },
})
).release_at?.at_time,
@ -152,7 +160,9 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
let data: VaultCapData[] = []
const getBatch = async (startAfter?: VaultBaseForString): Promise<void> => {
const batch = await creditManagerClient?.vaultsInfo({ limit: 5, startAfter })
const batch: ArrayOfVaultInfoResponse = await creditManagerClient.query({
vaults_info: { limit: 5, start_after: startAfter },
})
const batchProcessed = batch?.map(
(vaultInfo) =>
@ -184,19 +194,20 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
const creditAccounts = await get().getCreditAccounts(options)
const client = get().client!
const userWalletAddress = get().userWalletAddress
const promises = creditAccounts.map(async (creditAccount) => {
const vaultAddress = creditAccount.vaults[0].vault.address
const vault = get().vaultConfigs.find((vault) => vault.address === vaultAddress)
const vaultClient = new MarsMockVaultClient(client, userWalletAddress, vaultAddress)
const vaultClient = new VaultClient(vaultAddress, client)
const amounts = getAmountsFromActiveVault(creditAccount.vaults[0].amount)
return {
locked: Number(
await vaultClient.previewRedeem({
amount: amounts.locked,
await vaultClient.query({
preview_redeem: {
amount: amounts.locked,
},
}),
),
unlocking: amounts.unlocking,
@ -206,7 +217,7 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
}
})
const newLpTokens = await Promise.all(promises)
const newLpTokens = (await Promise.all(promises)).filter((lpToken) => !!lpToken.denom)
set({ lpTokens: newLpTokens })
return newLpTokens
@ -378,7 +389,11 @@ export const vaultsSlice = (set: NamedSet<Store>, get: GetState<Store>): VaultsS
vault: vaultTokenAmounts.locked,
},
values,
apy: apy,
apy: {
total: curr.apy,
borrow: trueBorrowRate,
net: apy,
},
currentLeverage: leverage,
ltv: leverageToLtv(leverage),
...(unlockTime ? { unlockAtTimestamp: unlockTime } : {}),

View File

@ -49,6 +49,14 @@ a {
color: $colorPrimaryHighlight;
text-decoration: underline;
}
&:has(button) {
&:hover,
&:focus,
&:active {
text-decoration: none;
}
}
}
b,

View File

@ -1,35 +1,35 @@
@use 'sass:math';
$rem-base: 16px;
$rem-base: 15px;
/* Colors */
$colorWhite: #f5f5f5;
$colorGrey: #bdbdbd;
$colorGreyLight: #e0e0e0;
$colorGreyHighlight: #efefef;
$colorGreyMedium: #9e9e9e;
$colorGreyDark: #616161;
$colorWhite: #ffffff;
$colorGrey: #3a3c49;
$colorGreyLight: #bfbfbf;
$colorGreyHighlight: #4c4c4c;
$colorGreyMedium: #5f697a;
$colorGreyDark: #1a1c25;
/* CI Colors */
$colorPrimary: #0000ff;
$colorPrimaryHighlight: #6962cc;
$colorPrimaryAlpha: rgba(0, 0, 255, 0.05);
$colorSecondary: #212121;
$colorSecondaryHighlight: #424242;
$colorSecondaryDark: #111111;
$colorSecondaryAlpha: rgba(17, 17, 17, 0.15);
$colorAccent: $colorGreyMedium;
$colorAccentHighlight: $colorGreyMedium;
$colorAccentDark: $colorGreyDark;
$colorAccentInverted: $colorGreyLight;
$colorPrimary: #14a693;
$colorPrimaryHighlight: #15bfa9;
$colorPrimaryAlpha: rgba(20, 166, 147, 0.15);
$colorSecondary: #524bb1;
$colorSecondaryHighlight: #6962cc;
$colorSecondaryDark: #440b37;
$colorSecondaryAlpha: rgba(68, 11, 55, 0.7);
$colorAccent: #2c1b2f;
$colorAccentHighlight: #421f32;
$colorAccentDark: #341a2a;
$colorAccentInverted: #345dff;
/* Info Colors */
$colorInfoProfit: #c4e7e9;
$colorInfoLoss: #c8aaaa;
$colorInfoWarning: #ffb5b5;
$colorInfoVoteAgainst: #6c5a46;
$colorInfoProfit: #41a4a9;
$colorInfoLoss: #f96363;
$colorInfoWarning: #c83333;
$colorInfoVoteAgainst: #eb9e49;
/* Token Colors */
$colorTokenMARS: #dd5b65;
$colorTokenMARS: #a03b45;
$colorTokenOSMO: #9f1ab9;
$colorTokenATOM: #6f7390;
$colorTokenAxlUSDC: #478edc;
@ -60,29 +60,32 @@ $alphaBlack80: rgba(0, 0, 0, 0.8);
$alphaBlack90: rgba(0, 0, 0, 0.9);
/* Background Colors */
$backgroundBody: $colorGrey;
$backgroundBodyDark: $backgroundBody;
$backgroundInTile: transparent;
$backgroundFooter: transparent;
$backgroundBody: #562a3b;
$backgroundBodyDark: #141621;
$backgroundInTile: $alphaBlack30;
$backgroundFooter: $alphaBlack20;
/* Slider Colors */
$sliderThumb: $colorGreyDark;
$sliderMark: $colorGreyDark;
$sliderThumb: $colorWhite;
$sliderMark: $colorGreyLight;
/* Tooltip Colors */
$tooltipIconColor: $alphaBlack60;
$tooltipIconColor: $alphaWhite20;
$tableSort: $alphaWhite20;
$tableSortActive: $colorWhite;
$tableHeader: $alphaWhite50;
/* Table Colors */
$tableBorder: $alphaBlack30;
$tableBorderEnd: $alphaBlack80;
$tableSort: $alphaBlack20;
$tableSortActive: $alphaBlack90;
$tableHeader: $alphaBlack40;
$tableLabel: $colorSecondaryDark;
$tableBorder: $alphaWhite10;
$tableBorderEnd: $alphaWhite80;
$tableSort: $alphaWhite20;
$tableSortActive: $colorWhite;
$tableHeader: $alphaWhite40;
$tableLabel: $alphaWhite60;
/* Graph Colors */
$graphLiquidationsLine: $alphaBlack70;
$graphAxis: $alphaBlack40;
$graphLiquidationsLine: $alphaWhite70;
$graphAxis: $alphaWhite40;
/* Shadows */
$shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
@ -93,40 +96,43 @@ $shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
/* Devider */
@mixin devider10 {
border-bottom: 1px solid $alphaBlack10;
border-bottom: 1px solid $alphaWhite10;
}
@mixin devider20 {
border-bottom: 1px solid $alphaBlack20;
border-bottom: 1px solid $alphaWhite20;
}
@mixin devider40 {
border-bottom: 1px solid $alphaBlack40;
border-bottom: 1px solid $alphaWhite40;
}
@mixin devider60 {
border-bottom: 1px solid $alphaBlack60;
border-bottom: 1px solid $alphaWhite60;
}
/* Backgrounds */
@mixin bgBody {
background-color: $backgroundBody;
}
@mixin bgTableHover {
background-color: transparent;
background-size: 100% auto;
background-image: url('../images/bg.svg');
background-position: center top;
}
@mixin bgBodyDark {
background-color: $backgroundBodyDark;
}
@mixin bgTableHover {
background-color: rgba($colorPrimary, 0.2);
}
@mixin bgProposalActive {
background: linear-gradient(90deg, #10aa93 2.6%, #248aa9 97.92%);
}
@mixin bgProposalHover {
background-color: $colorGreyMedium;
background-color: #05252f;
}
@mixin bgTile($deg: 99.79) {
@ -138,7 +144,7 @@ $shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
}
@mixin bgInTile {
background: $backgroundInTile;
background: $alphaBlack30;
}
@mixin bgOverlay {
@ -155,11 +161,11 @@ $shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
}
@mixin bgTileDevider {
background-color: $alphaBlack60;
background-color: $alphaWhite60;
}
@mixin bgDevider {
background-color: $alphaBlack20;
background-color: $alphaWhite20;
}
@mixin bgInput {
@ -168,60 +174,68 @@ $shadowInset: inset 0px 2px 2px rgba(0, 0, 0, 0.25);
@mixin bgPrimary {
background-color: $colorPrimary;
color: $colorWhite;
}
@mixin bgSecondary {
background-color: $colorSecondary;
color: $colorWhite;
}
@mixin bgTertiary {
background-color: $alphaBlack60;
color: $colorWhite;
background-color: rgba(82, 75, 177, 0.5);
}
@mixin bgLimit {
background: $colorPrimary;
background: linear-gradient(
to right,
#15bfa9 20.9%,
#5e4bb1 49.68%,
#382685 82.55%,
#c83333 100%
);
}
@mixin bgLimitOpacity {
background: $colorPrimary;
background: linear-gradient(
to right,
#15bfa830 20.9%,
#5e4bb130 49.68%,
#38268530 82.55%,
#c8333330 100%
);
}
@mixin bgHatched {
background-image: linear-gradient(
135deg,
#1a1c25 33.33%,
rgba(255, 255, 255, 0.2) 33.33%,
rgba(255, 255, 255, 0.2) 50%,
#1a1c25 50%,
#1a1c25 83.33%,
rgba(255, 255, 255, 0.2) 83.33%,
rgba(255, 255, 255, 0.2) 100%
transparent 33.33%,
#826d6b 33.33%,
#826d6b 50%,
transparent 50%,
transparent 83.33%,
#826d6b 83.33%,
#826d6b 100%
);
background-size: 5px 5px;
}
/* GLOWS */
/* GLOWS */
@mixin glowXS {
display: none;
filter: blur(1px);
}
@mixin glowS {
display: none;
filter: blur(3px);
}
@mixin glowM {
display: none;
filter: blur(4px);
}
@mixin glowL {
display: none;
filter: blur(5px);
}
@mixin glowXL {
display: none;
filter: blur(8px);
}
@mixin glowXXL {
display: none;
filter: blur(24px);
}
/* Typography */
@ -229,11 +243,11 @@ $fontWeightLight: 300;
$fontWeightRegular: 400;
$fontWeightSemibold: 600;
$fontColorDarkPrimary: $colorWhite;
$fontColorDarkSecondary: $colorSecondaryDark;
$fontColorLightPrimary: $colorSecondaryDark;
$fontColorLightSecondary: $alphaBlack30;
$fontColorLightTertiary: $colorSecondaryDark;
$fontColorDarkPrimary: $colorSecondaryDark;
$fontColorDarkSecondary: rgba(68, 8, 55, 0.7);
$fontColorLightPrimary: $colorWhite;
$fontColorLightSecondary: $alphaWhite60;
$fontColorLightTertiary: rgba(255, 255, 255, 0.4);
$fontColorLtv: $colorWhite;
@mixin typoH1 {
@ -249,6 +263,8 @@ $fontColorLtv: $colorWhite;
@mixin typoH2caps {
@include typoH2;
text-transform: uppercase;
letter-spacing: rem-calc(9);
}
@mixin typoH3 {
@ -258,6 +274,8 @@ $fontColorLtv: $colorWhite;
@mixin typoH3caps {
font-size: rem-calc(30.42);
line-height: space(10);
text-transform: uppercase;
}
@mixin typoH4 {
@ -268,6 +286,8 @@ $fontColorLtv: $colorWhite;
@mixin typoH4caps {
@include typoH4;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoXXL {
@ -277,7 +297,9 @@ $fontColorLtv: $colorWhite;
@mixin typoXXLcaps {
@include typoXXL;
font-weight: $fontWeightLight;
font-weight: $fontWeightRegular;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoXL {
@ -287,6 +309,8 @@ $fontColorLtv: $colorWhite;
@mixin typoXLcaps {
@include typoXL;
letter-spacing: rem-calc(5);
text-transform: uppercase;
font-weight: $fontWeightLight;
}
@ -298,6 +322,8 @@ $fontColorLtv: $colorWhite;
@mixin typoLcaps {
@include typoL;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoM {
@ -307,6 +333,7 @@ $fontColorLtv: $colorWhite;
@mixin typoMcaps {
@include typoM;
text-transform: uppercase;
}
@mixin typoS {
@ -317,6 +344,8 @@ $fontColorLtv: $colorWhite;
@mixin typoScaps {
@include typoS;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoXS {
@ -327,6 +356,8 @@ $fontColorLtv: $colorWhite;
@mixin typoXScaps {
@include typoXS;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(3);
}
@mixin typoXXS {
@ -337,6 +368,8 @@ $fontColorLtv: $colorWhite;
@mixin typoXXScaps {
@include typoXXS;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(2);
}
@mixin typoXXXS {
@ -347,15 +380,20 @@ $fontColorLtv: $colorWhite;
@mixin typoXXXScaps {
@include typoXXXS;
font-weight: $fontWeightSemibold;
text-transform: uppercase;
letter-spacing: rem-calc(2);
}
@mixin typoButton {
font-family: Inter, sans-serif;
@include typoS;
font-weight: $fontWeightSemibold;
}
@mixin typoNav {
@include typoL;
text-transform: uppercase;
letter-spacing: rem-calc(5);
}
@mixin typoNetwork {
@ -429,70 +467,100 @@ $spacingBase: 4;
/* LAYOUTS */
@mixin layoutTile {
padding: space(1);
background: $colorGreyHighlight;
border: 2px solid $colorWhite;
box-shadow: 0 0 0 3px $colorGreyHighlight, 12px 12px 0 0 rgb(0 0 0 / 50%) !important;
@include bgTile;
border: rem-calc(7) solid $colorAccentHighlight;
border-radius: $borderRadiusXL;
height: fit-content;
}
@mixin layoutTooltip {
padding: space(3);
background: $colorGreyLight;
border: 1px solid $colorSecondaryDark;
@include padding(2, 4);
@include bgTooltip;
@include typoS;
box-shadow: 0 rem-calc(3) rem-calc(4) rgba(0, 0, 0, 0.14),
0 rem-calc(3) rem-calc(3) rgba(0, 0, 0, 0.12), 0 rem-calc(1) rem-calc(8) rgba(0, 0, 0, 0.2);
border-radius: $borderRadiusL;
max-width: rem-calc(350);
}
@mixin layoutPopover {
padding: space(3);
background: $colorGreyLight;
border: 1px solid $colorSecondaryDark;
@include bgPopover;
box-shadow: 0 rem-calc(2) rem-calc(2) rgba(0, 0, 0, 0.14),
0 rem-calc(1) rem-calc(5) rgba(0, 0, 0, 0.2);
border-radius: $borderRadiusL;
}
@mixin layoutIncentiveButton {
--border-width: 3px;
background-color: #946582;
position: relative;
border: none;
margin: rem-calc(3) rem-calc(11) 0 0;
height: rem-calc(28);
&:hover {
border: none;
background-color: darken(#946582, 10%);
}
&::after {
border-radius: $borderRadiusXXL;
position: absolute;
content: '';
top: calc(-1 * var(--border-width));
left: calc(-1 * var(--border-width));
z-index: -1;
width: calc(100% + var(--border-width) * 2);
height: calc(100% + var(--border-width) * 2);
background: linear-gradient(
90deg,
rgba(105, 98, 204, 0.8) 0%,
rgba(105, 98, 204, 1) 40%,
rgba(255, 255, 255, 1) 50%,
rgba(105, 98, 204, 1) 60%,
rgba(105, 98, 204, 0.8) 100%
);
background-size: 300% 300%;
background-position: 0 50%;
animation: moveGradient 6s alternate infinite;
}
}
@mixin layoutLogo {
> svg {
width: rem-calc(57);
height: rem-calc(57);
path {
stroke: $fontColorLightPrimary;
}
width: rem-calc(50);
height: rem-calc(50);
}
}
@mixin layoutGlobal {
opacity: 1 !important;
box-shadow: none !important;
}
/* Buttons */
$buttonBorder: $alphaBlack40;
$buttonBorderHover: $colorSecondaryDark;
$buttonBorder: $alphaWhite40;
$buttonBorderHover: $colorWhite;
@mixin buttonS {
@include typoS;
@include padding(1.5, 5);
height: rem-calc(32);
min-height: rem-calc(32);
}
@mixin buttonM {
@include typoM;
@include padding(2.5, 6);
height: rem-calc(40);
min-height: rem-calc(40);
}
@mixin buttonL {
@include typoL;
@include padding(2.5, 6);
height: rem-calc(56);
min-height: rem-calc(56);
}
@mixin buttonSolidPrimary {
&.primary {
background-color: $colorPrimary;
color: $colorWhite;
&:hover,
&:focus {
@ -508,7 +576,6 @@ $buttonBorderHover: $colorSecondaryDark;
@mixin buttonSolidSecondary {
&.secondary {
background-color: $colorSecondary;
color: $colorWhite;
&:hover,
&:focus {
@ -523,33 +590,34 @@ $buttonBorderHover: $colorSecondaryDark;
@mixin buttonSolidTertiary {
&.tertiary {
background-color: $colorSecondaryDark;
color: $colorWhite;
border: 1px solid $alphaBlack30;
background-color: $colorSecondaryAlpha;
border: 1px solid $alphaWhite60;
&:hover,
&:focus {
border: 1px solid $alphaBlack20;
border: 1px solid $fontColorLightPrimary;
background-color: $colorSecondaryDark;
}
&:active {
border: 1px solid $fontColorLightPrimary;
background-color: lighten($colorSecondaryDark, 10%);
}
}
}
/* Border Radius */
$borderRadiusXXXS: 0;
$borderRadiusXXS: 0;
$borderRadiusXS: 0;
$borderRadiusS: 0;
$borderRadiusM: 0;
$borderRadiusL: 0;
$borderRadiusXL: 0;
$borderRadiusXXL: 0;
$borderRadiusXXXL: 0;
$borderRadiusXXXXL: 0;
$borderRadiusRound: 0;
$borderRadiusXXXS: rem-calc(3);
$borderRadiusXXS: rem-calc(4);
$borderRadiusXS: rem-calc(5);
$borderRadiusS: rem-calc(8);
$borderRadiusM: rem-calc(9);
$borderRadiusL: rem-calc(12);
$borderRadiusXL: rem-calc(16);
$borderRadiusXXL: rem-calc(20);
$borderRadiusXXXL: rem-calc(30);
$borderRadiusXXXXL: rem-calc(100);
$borderRadiusRound: 50%;
/* Dimensions */
$headerHeight: rem-calc(86);

View File

@ -1,6 +1,62 @@
import { Coin } from '@cosmjs/stargate'
import { WalletClient } from '@marsprotocol/wallet-connector'
import {
ArrayOfCoin,
QueryMsg as CreditQueryMsg,
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { QueryMsg as AccountQueryMsg } from './generated/mars-account-nft/MarsAccountNft.types'
import { QueryMsg as VaultQueryMsg } from './generated/mars-mock-vault/MarsMockVault.types'
export class SetupError extends Error {
constructor(name: string, message: string) {
super(message)
this.name = name
}
}
export class CreditManagerClient {
address: string
client: WalletClient
constructor(address: string, client: WalletClient) {
this.address = address
this.client = client
}
query(message: CreditQueryMsg) {
return this.client.cosmWasmClient.queryContractSmart(this.address, message)
}
estimateWithdrawLiquidity({ lpToken }: { lpToken: Coin }) {
return Promise<ArrayOfCoin>
}
}
export class AccountNftClient {
address: string
client: WalletClient
constructor(address: string, client: WalletClient) {
this.address = address
this.client = client
}
query(message: AccountQueryMsg) {
return this.client.cosmWasmClient.queryContractSmart(this.address, message)
}
}
export class VaultClient {
address: string
client: WalletClient
constructor(address: string, client: WalletClient) {
this.address = address
this.client = client
}
query(message: VaultQueryMsg) {
return this.client.cosmWasmClient.queryContractSmart(this.address, message)
}
}

View File

@ -7,6 +7,7 @@ export enum QUERY_KEYS {
USER_BALANCE = 'userBalance',
USER_DEBT = 'userDebt',
USER_DEPOSIT = 'userDeposits',
USER_ICNS = 'userIcns',
ATOM_PRICE = 'atomPrice',
SAFETY_FUND_BALANCE = 'safetyFundBalance',
MARKET_DEPOSITS = 'marketDeposits',

View File

@ -13,11 +13,11 @@ import {
ApprovalResponse,
ApprovalsResponse,
Binary,
ConfigBaseForString,
ConfigUpdates,
ContractInfoResponse,
Expiration,
MinterResponse,
NftConfigBaseForString,
NftConfigUpdates,
NftInfoResponseForEmpty,
NumTokensResponse,
OperatorsResponse,
@ -27,7 +27,7 @@ import {
} from './MarsAccountNft.types'
export interface MarsAccountNftReadOnlyInterface {
contractAddress: string
config: () => Promise<ConfigBaseForString>
config: () => Promise<NftConfigBaseForString>
nextId: () => Promise<Uint64>
ownerOf: ({
includeExpired,
@ -113,7 +113,7 @@ export class MarsAccountNftQueryClient implements MarsAccountNftReadOnlyInterfac
this.minter = this.minter.bind(this)
}
config = async (): Promise<ConfigBaseForString> => {
config = async (): Promise<NftConfigBaseForString> => {
return this.client.queryContractSmart(this.contractAddress, {
config: {},
})
@ -263,7 +263,7 @@ export interface MarsAccountNftInterface extends MarsAccountNftReadOnlyInterface
{
updates,
}: {
updates: ConfigUpdates
updates: NftConfigUpdates
},
fee?: number | StdFee | 'auto',
memo?: string,
@ -398,7 +398,7 @@ export class MarsAccountNftClient
{
updates,
}: {
updates: ConfigUpdates
updates: NftConfigUpdates
},
fee: number | StdFee | 'auto' = 'auto',
memo?: string,

View File

@ -10,7 +10,7 @@ import { toUtf8 } from '@cosmjs/encoding'
import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx'
import { MsgExecuteContractEncodeObject } from 'cosmwasm'
import { Binary, ConfigUpdates, Expiration } from './MarsAccountNft.types'
import { Binary, Expiration, NftConfigUpdates } from './MarsAccountNft.types'
export interface MarsAccountNftMessage {
contractAddress: string
sender: string
@ -18,7 +18,7 @@ export interface MarsAccountNftMessage {
{
updates,
}: {
updates: ConfigUpdates
updates: NftConfigUpdates
},
funds?: Coin[],
) => MsgExecuteContractEncodeObject
@ -125,7 +125,7 @@ export class MarsAccountNftMessageComposer implements MarsAccountNftMessage {
{
updates,
}: {
updates: ConfigUpdates
updates: NftConfigUpdates
},
funds?: Coin[],
): MsgExecuteContractEncodeObject => {

View File

@ -15,11 +15,11 @@ import {
ApprovalResponse,
ApprovalsResponse,
Binary,
ConfigBaseForString,
ConfigUpdates,
ContractInfoResponse,
Expiration,
MinterResponse,
NftConfigBaseForString,
NftConfigUpdates,
NftInfoResponseForEmpty,
NumTokensResponse,
OperatorsResponse,
@ -329,12 +329,12 @@ export function useMarsAccountNftNextIdQuery<TData = Uint64>({
)
}
export interface MarsAccountNftConfigQuery<TData>
extends MarsAccountNftReactQuery<ConfigBaseForString, TData> {}
export function useMarsAccountNftConfigQuery<TData = ConfigBaseForString>({
extends MarsAccountNftReactQuery<NftConfigBaseForString, TData> {}
export function useMarsAccountNftConfigQuery<TData = NftConfigBaseForString>({
client,
options,
}: MarsAccountNftConfigQuery<TData>) {
return useQuery<ConfigBaseForString, Error, TData>(
return useQuery<NftConfigBaseForString, Error, TData>(
marsAccountNftQueryKeys.config(client?.contractAddress),
() => (client ? client.config() : Promise.reject(new Error('Invalid client'))),
{ ...options, enabled: !!client && (options?.enabled != undefined ? options.enabled : true) },
@ -545,7 +545,7 @@ export function useMarsAccountNftAcceptMinterRoleMutation(
export interface MarsAccountNftUpdateConfigMutation {
client: MarsAccountNftClient
msg: {
updates: ConfigUpdates
updates: NftConfigUpdates
}
args?: {
fee?: number | StdFee | 'auto'

View File

@ -15,7 +15,7 @@ export interface InstantiateMsg {
export type ExecuteMsg =
| {
update_config: {
updates: ConfigUpdates
updates: NftConfigUpdates
}
}
| {
@ -81,7 +81,7 @@ export type Expiration =
}
export type Timestamp = Uint64
export type Uint64 = string
export interface ConfigUpdates {
export interface NftConfigUpdates {
max_value_for_burn?: Uint128 | null
proposed_new_minter?: string | null
}
@ -183,7 +183,7 @@ export interface ApprovalResponse {
export interface ApprovalsResponse {
approvals: Approval[]
}
export interface ConfigBaseForString {
export interface NftConfigBaseForString {
max_value_for_burn: Uint128
proposed_new_minter?: string | null
}

View File

@ -13,6 +13,7 @@ import {
ArrayOfCoin,
ArrayOfCoinBalanceResponseItem,
ArrayOfDebtShares,
ArrayOfLentShares,
ArrayOfSharesResponseItem,
ArrayOfString,
ArrayOfVaultInfoResponse,
@ -23,6 +24,8 @@ import {
ConfigUpdates,
DebtShares,
HealthResponse,
LentShares,
NftConfigUpdates,
Positions,
Uint128,
VaultBaseForString,
@ -68,6 +71,21 @@ export interface MarsCreditManagerReadOnlyInterface {
limit?: number
startAfter?: string
}) => Promise<ArrayOfDebtShares>
allLentShares: ({
limit,
startAfter,
}: {
limit?: number
startAfter?: string[][]
}) => Promise<ArrayOfSharesResponseItem>
totalLentShares: () => Promise<LentShares>
allTotalLentShares: ({
limit,
startAfter,
}: {
limit?: number
startAfter?: string
}) => Promise<ArrayOfLentShares>
allVaultPositions: ({
limit,
startAfter,
@ -108,6 +126,9 @@ export class MarsCreditManagerQueryClient implements MarsCreditManagerReadOnlyIn
this.allDebtShares = this.allDebtShares.bind(this)
this.totalDebtShares = this.totalDebtShares.bind(this)
this.allTotalDebtShares = this.allTotalDebtShares.bind(this)
this.allLentShares = this.allLentShares.bind(this)
this.totalLentShares = this.totalLentShares.bind(this)
this.allTotalLentShares = this.allTotalLentShares.bind(this)
this.allVaultPositions = this.allVaultPositions.bind(this)
this.totalVaultCoinBalance = this.totalVaultCoinBalance.bind(this)
this.allTotalVaultCoinBalances = this.allTotalVaultCoinBalances.bind(this)
@ -209,6 +230,39 @@ export class MarsCreditManagerQueryClient implements MarsCreditManagerReadOnlyIn
},
})
}
allLentShares = async ({
limit,
startAfter,
}: {
limit?: number
startAfter?: string[][]
}): Promise<ArrayOfSharesResponseItem> => {
return this.client.queryContractSmart(this.contractAddress, {
all_lent_shares: {
limit,
start_after: startAfter,
},
})
}
totalLentShares = async (): Promise<LentShares> => {
return this.client.queryContractSmart(this.contractAddress, {
total_lent_shares: {},
})
}
allTotalLentShares = async ({
limit,
startAfter,
}: {
limit?: number
startAfter?: string
}): Promise<ArrayOfLentShares> => {
return this.client.queryContractSmart(this.contractAddress, {
all_total_lent_shares: {
limit,
start_after: startAfter,
},
})
}
allVaultPositions = async ({
limit,
startAfter,
@ -288,9 +342,9 @@ export interface MarsCreditManagerInterface extends MarsCreditManagerReadOnlyInt
) => Promise<ExecuteResult>
updateConfig: (
{
newConfig,
updates,
}: {
newConfig: ConfigUpdates
updates: ConfigUpdates
},
fee?: number | StdFee | 'auto',
memo?: string,
@ -301,6 +355,16 @@ export interface MarsCreditManagerInterface extends MarsCreditManagerReadOnlyInt
memo?: string,
funds?: Coin[],
) => Promise<ExecuteResult>
updateNftConfig: (
{
updates,
}: {
updates: NftConfigUpdates
},
fee?: number | StdFee | 'auto',
memo?: string,
funds?: Coin[],
) => Promise<ExecuteResult>
callback: (
fee?: number | StdFee | 'auto',
memo?: string,
@ -324,6 +388,7 @@ export class MarsCreditManagerClient
this.updateCreditAccount = this.updateCreditAccount.bind(this)
this.updateConfig = this.updateConfig.bind(this)
this.updateOwner = this.updateOwner.bind(this)
this.updateNftConfig = this.updateNftConfig.bind(this)
this.callback = this.callback.bind(this)
}
@ -371,9 +436,9 @@ export class MarsCreditManagerClient
}
updateConfig = async (
{
newConfig,
updates,
}: {
newConfig: ConfigUpdates
updates: ConfigUpdates
},
fee: number | StdFee | 'auto' = 'auto',
memo?: string,
@ -384,7 +449,7 @@ export class MarsCreditManagerClient
this.contractAddress,
{
update_config: {
new_config: newConfig,
updates,
},
},
fee,
@ -408,6 +473,29 @@ export class MarsCreditManagerClient
funds,
)
}
updateNftConfig = async (
{
updates,
}: {
updates: NftConfigUpdates
},
fee: number | StdFee | 'auto' = 'auto',
memo?: string,
funds?: Coin[],
): Promise<ExecuteResult> => {
return await this.client.execute(
this.sender,
this.contractAddress,
{
update_nft_config: {
updates,
},
},
fee,
memo,
funds,
)
}
callback = async (
fee: number | StdFee | 'auto' = 'auto',
memo?: string,

Some files were not shown because too many files have changed in this diff Show More