mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Dynamic token list support for publishing (#1516)
* feat: add approved tokens list query to subgraph * feat: add base token selector * feat: add placeholder tooltip message for base token * feat: use user selected base token for publish * fix: publish constants * feat: update base token query to include digits and symbol * feat: display correct token name and symbol in publish pricing tab * fix: publish preview token name * fix: query type * feat: add balance fetch for all approved tokens * fix: balance check for dynamic price with alternative base tokens * feat: update balance to show baseToken instead of ocean * fix: default baseToken in publish form * feat: update text content for pricing publish step * chore: update ocean.js * add decimals to token Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix dt decimals Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * update ocean.js Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro> * fix: show correct basetoken symbol under button buy * refactor: move baseToken selector to input label [WIP] * refactor: preserve baseToken value value when switching tabs * remove basetoken tooltip from content json * fix: price props * refactor: remove BaseToken component * fix: baseToken name on first load * fix: baseToken display name in dynamic price * fix: conversion tooltip text * fix: error box overlapping in Coin component * feat: add token logo component * feat: add basetoken logo to asset actions pool * fix: token images size * fix: add default appproved token list when disconnected or chainId not supported * fix: datatoken logo on asset details meta * refactor: balance fetch + move approved base tokens list in web3 provider * feat: update all datatokens to display ocean logo in violet * fix: show correct logos on polygon * fix wallet, remove dynamic * fix build * fix: reset baseToken on chainId change during publish * fix: price tabs selection indicator * feat: set the ocean token as default in pricing * add baseToken * fix price * remove firstPrice * cleanup, more affordance for token dropdown Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro> Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
eb29c4ce3b
commit
52ad877b13
@ -2,7 +2,7 @@
|
|||||||
"create": {
|
"create": {
|
||||||
"fixed": {
|
"fixed": {
|
||||||
"title": "Fixed",
|
"title": "Fixed",
|
||||||
"info": "Set your price for accessing this data set. The datatoken for this data set will be worth the entered amount of OCEAN.",
|
"info": "Set your price for accessing this data set. The datatoken for this data set will be worth the entered amount of the selected base token.",
|
||||||
"tooltips": {
|
"tooltips": {
|
||||||
"communityFee": "Goes to Ocean DAO for teams to improve the tools, build apps, do outreach, and more. A small fraction is used to burn OCEAN. This fee is collected when downloading or using an asset in a compute job.",
|
"communityFee": "Goes to Ocean DAO for teams to improve the tools, build apps, do outreach, and more. A small fraction is used to burn OCEAN. This fee is collected when downloading or using an asset in a compute job.",
|
||||||
"marketplaceFee": "Goes to the marketplace owner that is hosting and providing the marketplace and is collected when downloading or using an asset in a compute job. In Ocean Market, it is treated as network revenue that goes to the Ocean community."
|
"marketplaceFee": "Goes to the marketplace owner that is hosting and providing the marketplace and is collected when downloading or using an asset in a compute job. In Ocean Market, it is treated as network revenue that goes to the Ocean community."
|
||||||
|
@ -6,7 +6,10 @@ export const opcQuery = gql`
|
|||||||
swapOceanFee
|
swapOceanFee
|
||||||
swapNonOceanFee
|
swapNonOceanFee
|
||||||
approvedTokens {
|
approvedTokens {
|
||||||
id
|
address: id
|
||||||
|
symbol
|
||||||
|
name
|
||||||
|
decimals
|
||||||
}
|
}
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,9 @@ function MarketMetadataProvider({
|
|||||||
|
|
||||||
opcData.push({
|
opcData.push({
|
||||||
chainId: appConfig.chainIdsSupported[i],
|
chainId: appConfig.chainIdsSupported[i],
|
||||||
approvedTokens: response.data?.opc.approvedTokens?.map((x) => x.id),
|
approvedTokens: response.data?.opc.approvedTokens.map(
|
||||||
|
(token) => token.address
|
||||||
|
),
|
||||||
swapApprovedFee: response.data?.opc.swapOceanFee,
|
swapApprovedFee: response.data?.opc.swapOceanFee,
|
||||||
swapNotApprovedFee: response.data?.opc.swapNonOceanFee
|
swapNotApprovedFee: response.data?.opc.swapNonOceanFee
|
||||||
} as OpcFee)
|
} as OpcFee)
|
||||||
|
@ -14,7 +14,6 @@ import WalletConnectProvider from '@walletconnect/web3-provider'
|
|||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||||
import { isBrowser } from '@utils/index'
|
import { isBrowser } from '@utils/index'
|
||||||
import { getEnsName } from '@utils/ens'
|
import { getEnsName } from '@utils/ens'
|
||||||
import { getOceanBalance } from '@utils/ocean'
|
|
||||||
import useNetworkMetadata, {
|
import useNetworkMetadata, {
|
||||||
getNetworkDataById,
|
getNetworkDataById,
|
||||||
getNetworkDisplayName,
|
getNetworkDisplayName,
|
||||||
@ -22,9 +21,12 @@ import useNetworkMetadata, {
|
|||||||
NetworkType
|
NetworkType
|
||||||
} from '../@hooks/useNetworkMetadata'
|
} from '../@hooks/useNetworkMetadata'
|
||||||
import { useMarketMetadata } from './MarketMetadata'
|
import { useMarketMetadata } from './MarketMetadata'
|
||||||
|
import { getTokenBalance } from '@utils/web3'
|
||||||
|
import { getOpcsApprovedTokens } from '@utils/subgraph'
|
||||||
|
|
||||||
interface Web3ProviderValue {
|
interface Web3ProviderValue {
|
||||||
web3: Web3
|
web3: Web3
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
web3Provider: any
|
web3Provider: any
|
||||||
web3Modal: Web3Modal
|
web3Modal: Web3Modal
|
||||||
web3ProviderInfo: IProviderInfo
|
web3ProviderInfo: IProviderInfo
|
||||||
@ -39,6 +41,7 @@ interface Web3ProviderValue {
|
|||||||
isTestnet: boolean
|
isTestnet: boolean
|
||||||
web3Loading: boolean
|
web3Loading: boolean
|
||||||
isSupportedOceanNetwork: boolean
|
isSupportedOceanNetwork: boolean
|
||||||
|
approvedBaseTokens: TokenInfo[]
|
||||||
connect: () => Promise<void>
|
connect: () => Promise<void>
|
||||||
logout: () => Promise<void>
|
logout: () => Promise<void>
|
||||||
}
|
}
|
||||||
@ -65,16 +68,6 @@ const providerOptions = isBrowser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// torus: {
|
|
||||||
// package: require('@toruslabs/torus-embed')
|
|
||||||
// // options: {
|
|
||||||
// // networkParams: {
|
|
||||||
// // host: oceanConfig.url, // optional
|
|
||||||
// // chainId: 1337, // optional
|
|
||||||
// // networkId: 1337 // optional
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
: {}
|
: {}
|
||||||
|
|
||||||
@ -93,7 +86,9 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
const { appConfig } = useMarketMetadata()
|
const { appConfig } = useMarketMetadata()
|
||||||
|
|
||||||
const [web3, setWeb3] = useState<Web3>()
|
const [web3, setWeb3] = useState<Web3>()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const [web3Provider, setWeb3Provider] = useState<any>()
|
const [web3Provider, setWeb3Provider] = useState<any>()
|
||||||
|
|
||||||
const [web3Modal, setWeb3Modal] = useState<Web3Modal>()
|
const [web3Modal, setWeb3Modal] = useState<Web3Modal>()
|
||||||
const [web3ProviderInfo, setWeb3ProviderInfo] = useState<IProviderInfo>()
|
const [web3ProviderInfo, setWeb3ProviderInfo] = useState<IProviderInfo>()
|
||||||
const [networkId, setNetworkId] = useState<number>()
|
const [networkId, setNetworkId] = useState<number>()
|
||||||
@ -106,10 +101,10 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
const [accountEns, setAccountEns] = useState<string>()
|
const [accountEns, setAccountEns] = useState<string>()
|
||||||
const [web3Loading, setWeb3Loading] = useState<boolean>(true)
|
const [web3Loading, setWeb3Loading] = useState<boolean>(true)
|
||||||
const [balance, setBalance] = useState<UserBalance>({
|
const [balance, setBalance] = useState<UserBalance>({
|
||||||
eth: '0',
|
eth: '0'
|
||||||
ocean: '0'
|
|
||||||
})
|
})
|
||||||
const [isSupportedOceanNetwork, setIsSupportedOceanNetwork] = useState(true)
|
const [isSupportedOceanNetwork, setIsSupportedOceanNetwork] = useState(true)
|
||||||
|
const [approvedBaseTokens, setApprovedBaseTokens] = useState<TokenInfo[]>()
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Helper: connect to web3
|
// Helper: connect to web3
|
||||||
@ -148,6 +143,19 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
}
|
}
|
||||||
}, [web3Modal])
|
}, [web3Modal])
|
||||||
|
|
||||||
|
// -----------------------------------
|
||||||
|
// Helper: Get approved base tokens list
|
||||||
|
// -----------------------------------
|
||||||
|
const getApprovedBaseTokens = useCallback(async (chainId: number) => {
|
||||||
|
try {
|
||||||
|
const approvedTokensList = await getOpcsApprovedTokens(chainId)
|
||||||
|
setApprovedBaseTokens(approvedTokensList)
|
||||||
|
LoggerInstance.log('[web3] Approved baseTokens', approvedTokensList)
|
||||||
|
} catch (error) {
|
||||||
|
LoggerInstance.error('[web3] Error: ', error.message)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Helper: Get user balance
|
// Helper: Get user balance
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
@ -155,16 +163,30 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
if (!accountId || !networkId || !web3) return
|
if (!accountId || !networkId || !web3) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const balance = {
|
const balance: UserBalance = {
|
||||||
eth: web3.utils.fromWei(await web3.eth.getBalance(accountId, 'latest')),
|
eth: web3.utils.fromWei(await web3.eth.getBalance(accountId, 'latest'))
|
||||||
ocean: await getOceanBalance(accountId, networkId, web3)
|
|
||||||
}
|
}
|
||||||
|
if (approvedBaseTokens?.length > 0) {
|
||||||
|
await Promise.all(
|
||||||
|
approvedBaseTokens.map(async (token) => {
|
||||||
|
const { address, decimals, symbol } = token
|
||||||
|
const tokenBalance = await getTokenBalance(
|
||||||
|
accountId,
|
||||||
|
decimals,
|
||||||
|
address,
|
||||||
|
web3
|
||||||
|
)
|
||||||
|
balance[symbol.toLocaleLowerCase()] = tokenBalance
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
setBalance(balance)
|
setBalance(balance)
|
||||||
LoggerInstance.log('[web3] Balance: ', balance)
|
LoggerInstance.log('[web3] Balance: ', balance)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
LoggerInstance.error('[web3] Error: ', error.message)
|
LoggerInstance.error('[web3] Error: ', error.message)
|
||||||
}
|
}
|
||||||
}, [accountId, networkId, web3])
|
}, [accountId, approvedBaseTokens, networkId, web3])
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Helper: Get user ENS name
|
// Helper: Get user ENS name
|
||||||
@ -227,6 +249,14 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
connectCached()
|
connectCached()
|
||||||
}, [connect, web3Modal])
|
}, [connect, web3Modal])
|
||||||
|
|
||||||
|
// -----------------------------------
|
||||||
|
// Get and set approved base tokens list
|
||||||
|
// -----------------------------------
|
||||||
|
useEffect(() => {
|
||||||
|
if (web3Loading) return
|
||||||
|
getApprovedBaseTokens(chainId || 1)
|
||||||
|
}, [chainId, getApprovedBaseTokens, web3Loading])
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Get and set user balance
|
// Get and set user balance
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
@ -303,9 +333,12 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
// Logout helper
|
// Logout helper
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
async function logout() {
|
async function logout() {
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
if (web3 && web3.currentProvider && (web3.currentProvider as any).close) {
|
if (web3 && web3.currentProvider && (web3.currentProvider as any).close) {
|
||||||
await (web3.currentProvider as any).close()
|
await (web3.currentProvider as any).close()
|
||||||
}
|
}
|
||||||
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
await web3Modal.clearCachedProvider()
|
await web3Modal.clearCachedProvider()
|
||||||
}
|
}
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
@ -354,6 +387,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
web3Provider.removeListener('networkChanged', handleNetworkChanged)
|
web3Provider.removeListener('networkChanged', handleNetworkChanged)
|
||||||
web3Provider.removeListener('accountsChanged', handleAccountsChanged)
|
web3Provider.removeListener('accountsChanged', handleAccountsChanged)
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [web3Provider, web3])
|
}, [web3Provider, web3])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -374,6 +408,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
isTestnet,
|
isTestnet,
|
||||||
web3Loading,
|
web3Loading,
|
||||||
isSupportedOceanNetwork,
|
isSupportedOceanNetwork,
|
||||||
|
approvedBaseTokens,
|
||||||
connect,
|
connect,
|
||||||
logout
|
logout
|
||||||
}}
|
}}
|
||||||
|
1
src/@types/Price.d.ts
vendored
1
src/@types/Price.d.ts
vendored
@ -50,6 +50,7 @@ declare global {
|
|||||||
|
|
||||||
interface PricePublishOptions {
|
interface PricePublishOptions {
|
||||||
price: number
|
price: number
|
||||||
|
baseToken: TokenInfo
|
||||||
type: 'fixed' | 'free'
|
type: 'fixed' | 'free'
|
||||||
freeAgreement: boolean
|
freeAgreement: boolean
|
||||||
}
|
}
|
||||||
|
2
src/@types/TokenBalance.d.ts
vendored
2
src/@types/TokenBalance.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
interface UserBalance {
|
interface UserBalance {
|
||||||
eth: string
|
eth: string
|
||||||
ocean: string
|
[key: string]: string
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ const tokensPriceQuery = gql`
|
|||||||
symbol
|
symbol
|
||||||
name
|
name
|
||||||
address
|
address
|
||||||
|
decimals
|
||||||
}
|
}
|
||||||
datatoken {
|
datatoken {
|
||||||
symbol
|
symbol
|
||||||
@ -122,6 +123,7 @@ const tokenPriceQuery = gql`
|
|||||||
symbol
|
symbol
|
||||||
name
|
name
|
||||||
address
|
address
|
||||||
|
decimals
|
||||||
}
|
}
|
||||||
datatoken {
|
datatoken {
|
||||||
symbol
|
symbol
|
||||||
@ -187,7 +189,8 @@ function getAccessDetailsFromTokenPrice(
|
|||||||
accessDetails.baseToken = {
|
accessDetails.baseToken = {
|
||||||
address: fixed.baseToken.address,
|
address: fixed.baseToken.address,
|
||||||
name: fixed.baseToken.name,
|
name: fixed.baseToken.name,
|
||||||
symbol: fixed.baseToken.symbol
|
symbol: fixed.baseToken.symbol,
|
||||||
|
decimals: fixed.baseToken.decimals
|
||||||
}
|
}
|
||||||
accessDetails.datatoken = {
|
accessDetails.datatoken = {
|
||||||
address: fixed.datatoken.address,
|
address: fixed.datatoken.address,
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { ConfigHelper, LoggerInstance, Config } from '@oceanprotocol/lib'
|
import { ConfigHelper, Config } from '@oceanprotocol/lib'
|
||||||
// import contractAddresses from '@oceanprotocol/contracts/artifacts/address.json'
|
// import contractAddresses from '@oceanprotocol/contracts/artifacts/address.json'
|
||||||
import { AbiItem } from 'web3-utils/types'
|
|
||||||
import Web3 from 'web3'
|
|
||||||
|
|
||||||
export function getOceanConfig(network: string | number): Config {
|
export function getOceanConfig(network: string | number): Config {
|
||||||
const config = new ConfigHelper().getConfig(
|
const config = new ConfigHelper().getConfig(
|
||||||
@ -30,45 +28,3 @@ export function getDevelopmentConfig(): Config {
|
|||||||
subgraphUri: 'https://v4.subgraph.rinkeby.oceanprotocol.com'
|
subgraphUri: 'https://v4.subgraph.rinkeby.oceanprotocol.com'
|
||||||
} as Config
|
} as Config
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOceanBalance(
|
|
||||||
accountId: string,
|
|
||||||
networkId: number,
|
|
||||||
web3: Web3
|
|
||||||
): Promise<string> {
|
|
||||||
const minABI = [
|
|
||||||
{
|
|
||||||
constant: true,
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
name: '_owner',
|
|
||||||
type: 'address'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
name: 'balanceOf',
|
|
||||||
outputs: [
|
|
||||||
{
|
|
||||||
name: 'balance',
|
|
||||||
type: 'uint256'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
payable: false,
|
|
||||||
stateMutability: 'view',
|
|
||||||
type: 'function'
|
|
||||||
}
|
|
||||||
] as AbiItem[]
|
|
||||||
|
|
||||||
try {
|
|
||||||
const token = new web3.eth.Contract(
|
|
||||||
minABI,
|
|
||||||
getOceanConfig(networkId).oceanTokenAddress,
|
|
||||||
{ from: accountId }
|
|
||||||
)
|
|
||||||
const result = web3.utils.fromWei(
|
|
||||||
await token.methods.balanceOf(accountId).call()
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
} catch (e) {
|
|
||||||
LoggerInstance.error(`ERROR: Failed to get the balance: ${e.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -57,7 +57,7 @@ export async function order(
|
|||||||
_consumeMarketFee: {
|
_consumeMarketFee: {
|
||||||
consumeMarketFeeAddress: marketFeeAddress,
|
consumeMarketFeeAddress: marketFeeAddress,
|
||||||
consumeMarketFeeAmount: consumeMarketOrderFee,
|
consumeMarketFeeAmount: consumeMarketOrderFee,
|
||||||
consumeMarketFeeToken: config.oceanTokenAddress
|
consumeMarketFeeToken: asset.accessDetails.baseToken.address
|
||||||
}
|
}
|
||||||
} as OrderParams
|
} as OrderParams
|
||||||
|
|
||||||
|
@ -66,6 +66,19 @@ const OpcFeesQuery = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const OpcsApprovedTokensQuery = gql`
|
||||||
|
query OpcsApprovedTokensQuery {
|
||||||
|
opcs {
|
||||||
|
approvedTokens {
|
||||||
|
address: id
|
||||||
|
symbol
|
||||||
|
name
|
||||||
|
decimals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export function getSubgraphUri(chainId: number): string {
|
export function getSubgraphUri(chainId: number): string {
|
||||||
const config = getOceanConfig(chainId)
|
const config = getOceanConfig(chainId)
|
||||||
return config.subgraphUri
|
return config.subgraphUri
|
||||||
@ -221,3 +234,17 @@ export async function getTopAssetsPublishers(
|
|||||||
|
|
||||||
return publishers.slice(0, nrItems)
|
return publishers.slice(0, nrItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getOpcsApprovedTokens(
|
||||||
|
chainId: number
|
||||||
|
): Promise<TokenInfo[]> {
|
||||||
|
const context = getQueryContext(chainId)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetchData(OpcsApprovedTokensQuery, null, context)
|
||||||
|
return response?.data?.opcs[0].approvedTokens
|
||||||
|
} catch (error) {
|
||||||
|
LoggerInstance.error('Error getOpcsApprovedTokens: ', error.message)
|
||||||
|
throw Error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { getNetworkDisplayName } from '@hooks/useNetworkMetadata'
|
|||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { getOceanConfig } from './ocean'
|
import { getOceanConfig } from './ocean'
|
||||||
|
import { AbiItem } from 'web3-utils/types'
|
||||||
|
|
||||||
export function accountTruncate(account: string): string {
|
export function accountTruncate(account: string): string {
|
||||||
if (!account || account === '') return
|
if (!account || account === '') return
|
||||||
@ -110,3 +111,52 @@ export async function addTokenToWallet(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getTokenBalance(
|
||||||
|
accountId: string,
|
||||||
|
decimals: number,
|
||||||
|
tokenAddress: string,
|
||||||
|
web3: Web3
|
||||||
|
): Promise<string> {
|
||||||
|
const minABI = [
|
||||||
|
{
|
||||||
|
constant: true,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: '_owner',
|
||||||
|
type: 'address'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
name: 'balanceOf',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
name: 'balance',
|
||||||
|
type: 'uint256'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
payable: false,
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function'
|
||||||
|
}
|
||||||
|
] as AbiItem[]
|
||||||
|
|
||||||
|
try {
|
||||||
|
const token = new web3.eth.Contract(minABI, tokenAddress, {
|
||||||
|
from: accountId
|
||||||
|
})
|
||||||
|
const balance = await token.methods.balanceOf(accountId).call()
|
||||||
|
const adjustedDecimalsBalance = `${balance}${'0'.repeat(18 - decimals)}`
|
||||||
|
return web3.utils.fromWei(adjustedDecimalsBalance)
|
||||||
|
} catch (e) {
|
||||||
|
LoggerInstance.error(`ERROR: Failed to get the balance: ${e.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTokenBalanceFromSymbol(
|
||||||
|
balance: UserBalance,
|
||||||
|
symbol: string
|
||||||
|
): string {
|
||||||
|
if (!symbol) return
|
||||||
|
const baseTokenBalance = balance?.[symbol.toLocaleLowerCase()]
|
||||||
|
return baseTokenBalance || '0'
|
||||||
|
}
|
||||||
|
@ -38,6 +38,10 @@
|
|||||||
transition: 0.2s ease-out;
|
transition: 0.2s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo svg {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.button:hover .logo,
|
.button:hover .logo,
|
||||||
.button:focus .logo {
|
.button:focus .logo {
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
|
@ -3,6 +3,7 @@ import classNames from 'classnames/bind'
|
|||||||
import { addTokenToWallet } from '@utils/web3'
|
import { addTokenToWallet } from '@utils/web3'
|
||||||
import { useWeb3 } from '@context/Web3'
|
import { useWeb3 } from '@context/Web3'
|
||||||
import Button from '@shared/atoms/Button'
|
import Button from '@shared/atoms/Button'
|
||||||
|
import OceanLogo from '@images/logo.svg'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
@ -10,14 +11,12 @@ const cx = classNames.bind(styles)
|
|||||||
export default function AddToken({
|
export default function AddToken({
|
||||||
address,
|
address,
|
||||||
symbol,
|
symbol,
|
||||||
logo,
|
|
||||||
text,
|
text,
|
||||||
className,
|
className,
|
||||||
minimal
|
minimal
|
||||||
}: {
|
}: {
|
||||||
address: string
|
address: string
|
||||||
symbol: string
|
symbol: string
|
||||||
logo: string // needs to be a remote image
|
|
||||||
text?: string
|
text?: string
|
||||||
className?: string
|
className?: string
|
||||||
minimal?: boolean
|
minimal?: boolean
|
||||||
@ -33,7 +32,7 @@ export default function AddToken({
|
|||||||
async function handleAddToken() {
|
async function handleAddToken() {
|
||||||
if (!web3Provider) return
|
if (!web3Provider) return
|
||||||
|
|
||||||
await addTokenToWallet(web3Provider, address, symbol, logo)
|
await addTokenToWallet(web3Provider, address, symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -44,7 +43,9 @@ export default function AddToken({
|
|||||||
onClick={handleAddToken}
|
onClick={handleAddToken}
|
||||||
>
|
>
|
||||||
<span className={styles.logoWrap}>
|
<span className={styles.logoWrap}>
|
||||||
<img src={logo} className={styles.logo} width="16" height="16" />
|
<div className={styles.logo}>
|
||||||
|
<OceanLogo />
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className={styles.text}>{text || `Add ${symbol}`}</span>
|
<span className={styles.text}>{text || `Add ${symbol}`}</span>
|
||||||
|
@ -8,6 +8,7 @@ interface ButtonBuyProps {
|
|||||||
disabled: boolean
|
disabled: boolean
|
||||||
hasPreviousOrder: boolean
|
hasPreviousOrder: boolean
|
||||||
hasDatatoken: boolean
|
hasDatatoken: boolean
|
||||||
|
btSymbol: string
|
||||||
dtSymbol: string
|
dtSymbol: string
|
||||||
dtBalance: string
|
dtBalance: string
|
||||||
assetType: string
|
assetType: string
|
||||||
@ -33,6 +34,7 @@ interface ButtonBuyProps {
|
|||||||
// TODO: we need to take a look at these messages
|
// TODO: we need to take a look at these messages
|
||||||
|
|
||||||
function getConsumeHelpText(
|
function getConsumeHelpText(
|
||||||
|
btSymbol: string,
|
||||||
dtBalance: string,
|
dtBalance: string,
|
||||||
dtSymbol: string,
|
dtSymbol: string,
|
||||||
hasDatatoken: boolean,
|
hasDatatoken: boolean,
|
||||||
@ -48,9 +50,9 @@ function getConsumeHelpText(
|
|||||||
: hasPreviousOrder
|
: hasPreviousOrder
|
||||||
? `You bought this ${assetType} already allowing you to use it without paying again.`
|
? `You bought this ${assetType} already allowing you to use it without paying again.`
|
||||||
: hasDatatoken
|
: hasDatatoken
|
||||||
? `You own ${dtBalance} ${dtSymbol} allowing you to use this data set by spending 1 ${dtSymbol}, but without paying OCEAN again.`
|
? `You own ${dtBalance} ${dtSymbol} allowing you to use this data set by spending 1 ${dtSymbol}, but without paying ${btSymbol} again.`
|
||||||
: isBalanceSufficient === false
|
: isBalanceSufficient === false
|
||||||
? 'You do not have enough OCEAN in your wallet to purchase this asset.'
|
? `You do not have enough ${btSymbol} in your wallet to purchase this asset.`
|
||||||
: `For using this ${assetType}, you will buy 1 ${dtSymbol} and immediately spend it back to the publisher.`
|
: `For using this ${assetType}, you will buy 1 ${dtSymbol} and immediately spend it back to the publisher.`
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
@ -58,6 +60,7 @@ function getConsumeHelpText(
|
|||||||
function getComputeAssetHelpText(
|
function getComputeAssetHelpText(
|
||||||
hasPreviousOrder: boolean,
|
hasPreviousOrder: boolean,
|
||||||
hasDatatoken: boolean,
|
hasDatatoken: boolean,
|
||||||
|
btSymbol: string,
|
||||||
dtSymbol: string,
|
dtSymbol: string,
|
||||||
dtBalance: string,
|
dtBalance: string,
|
||||||
isConsumable: boolean,
|
isConsumable: boolean,
|
||||||
@ -73,6 +76,7 @@ function getComputeAssetHelpText(
|
|||||||
hasProviderFee?: boolean
|
hasProviderFee?: boolean
|
||||||
) {
|
) {
|
||||||
const computeAssetHelpText = getConsumeHelpText(
|
const computeAssetHelpText = getConsumeHelpText(
|
||||||
|
btSymbol,
|
||||||
dtBalance,
|
dtBalance,
|
||||||
dtSymbol,
|
dtSymbol,
|
||||||
hasDatatoken,
|
hasDatatoken,
|
||||||
@ -91,7 +95,7 @@ function getComputeAssetHelpText(
|
|||||||
: hasPreviousOrderSelectedComputeAsset
|
: hasPreviousOrderSelectedComputeAsset
|
||||||
? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.`
|
? `You already bought the selected ${selectedComputeAssetType}, allowing you to use it without paying again.`
|
||||||
: hasDatatokenSelectedComputeAsset
|
: hasDatatokenSelectedComputeAsset
|
||||||
? `You own ${dtBalanceSelectedComputeAsset} ${dtSymbolSelectedComputeAsset} allowing you to use the selected ${selectedComputeAssetType} by spending 1 ${dtSymbolSelectedComputeAsset}, but without paying OCEAN again.`
|
? `You own ${dtBalanceSelectedComputeAsset} ${dtSymbolSelectedComputeAsset} allowing you to use the selected ${selectedComputeAssetType} by spending 1 ${dtSymbolSelectedComputeAsset}, but without paying ${btSymbol} again.`
|
||||||
: isBalanceSufficient === false
|
: isBalanceSufficient === false
|
||||||
? ''
|
? ''
|
||||||
: `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher.`
|
: `Additionally, you will buy 1 ${dtSymbolSelectedComputeAsset} for the ${selectedComputeAssetType} and spend it back to its publisher.`
|
||||||
@ -107,6 +111,7 @@ export default function ButtonBuy({
|
|||||||
disabled,
|
disabled,
|
||||||
hasPreviousOrder,
|
hasPreviousOrder,
|
||||||
hasDatatoken,
|
hasDatatoken,
|
||||||
|
btSymbol,
|
||||||
dtSymbol,
|
dtSymbol,
|
||||||
dtBalance,
|
dtBalance,
|
||||||
assetType,
|
assetType,
|
||||||
@ -161,6 +166,7 @@ export default function ButtonBuy({
|
|||||||
<div className={styles.help}>
|
<div className={styles.help}>
|
||||||
{action === 'download'
|
{action === 'download'
|
||||||
? getConsumeHelpText(
|
? getConsumeHelpText(
|
||||||
|
btSymbol,
|
||||||
dtBalance,
|
dtBalance,
|
||||||
dtSymbol,
|
dtSymbol,
|
||||||
hasDatatoken,
|
hasDatatoken,
|
||||||
@ -173,6 +179,7 @@ export default function ButtonBuy({
|
|||||||
: getComputeAssetHelpText(
|
: getComputeAssetHelpText(
|
||||||
hasPreviousOrder,
|
hasPreviousOrder,
|
||||||
hasDatatoken,
|
hasDatatoken,
|
||||||
|
btSymbol,
|
||||||
dtSymbol,
|
dtSymbol,
|
||||||
dtBalance,
|
dtBalance,
|
||||||
isConsumable,
|
isConsumable,
|
||||||
|
@ -59,7 +59,7 @@ export default function Conversion({
|
|||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={styleClasses}
|
className={styleClasses}
|
||||||
title="Approximation based on current OCEAN spot price on Coingecko"
|
title="Approximation based on the current selected base token spot price on Coingecko"
|
||||||
>
|
>
|
||||||
{!hideApproximateSymbol && '≈ '}
|
{!hideApproximateSymbol && '≈ '}
|
||||||
<strong dangerouslySetInnerHTML={{ __html: priceConverted }} />{' '}
|
<strong dangerouslySetInnerHTML={{ __html: priceConverted }} />{' '}
|
||||||
|
@ -15,6 +15,7 @@ import Decimal from 'decimal.js'
|
|||||||
import { MAX_DECIMALS } from '@utils/constants'
|
import { MAX_DECIMALS } from '@utils/constants'
|
||||||
import { useMarketMetadata } from '@context/MarketMetadata'
|
import { useMarketMetadata } from '@context/MarketMetadata'
|
||||||
import Alert from '@shared/atoms/Alert'
|
import Alert from '@shared/atoms/Alert'
|
||||||
|
import { getTokenBalanceFromSymbol } from '@utils/web3'
|
||||||
|
|
||||||
export default function FormStartCompute({
|
export default function FormStartCompute({
|
||||||
algorithms,
|
algorithms,
|
||||||
@ -160,12 +161,16 @@ export default function FormStartCompute({
|
|||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!totalPrice || !balance?.ocean || !dtBalance) return
|
const baseTokenBalance = getTokenBalanceFromSymbol(
|
||||||
|
balance,
|
||||||
setIsBalanceSufficient(
|
asset?.accessDetails?.baseToken?.symbol
|
||||||
compareAsBN(balance.ocean, `${totalPrice}`) || Number(dtBalance) >= 1
|
|
||||||
)
|
)
|
||||||
}, [totalPrice, balance?.ocean, dtBalance])
|
|
||||||
|
if (!totalPrice || !baseTokenBalance || !dtBalance) return
|
||||||
|
setIsBalanceSufficient(
|
||||||
|
compareAsBN(baseTokenBalance, `${totalPrice}`) || Number(dtBalance) >= 1
|
||||||
|
)
|
||||||
|
}, [totalPrice, balance, dtBalance, asset?.accessDetails?.baseToken?.symbol])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form className={styles.form}>
|
<Form className={styles.form}>
|
||||||
@ -215,6 +220,7 @@ export default function FormStartCompute({
|
|||||||
}
|
}
|
||||||
hasPreviousOrder={hasPreviousOrder}
|
hasPreviousOrder={hasPreviousOrder}
|
||||||
hasDatatoken={hasDatatoken}
|
hasDatatoken={hasDatatoken}
|
||||||
|
btSymbol={asset?.accessDetails?.baseToken?.symbol}
|
||||||
dtSymbol={asset?.datatokens[0]?.symbol}
|
dtSymbol={asset?.datatokens[0]?.symbol}
|
||||||
dtBalance={dtBalance}
|
dtBalance={dtBalance}
|
||||||
assetTimeout={assetTimeout}
|
assetTimeout={assetTimeout}
|
||||||
|
@ -158,6 +158,7 @@ export default function Download({
|
|||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
hasPreviousOrder={isOwned}
|
hasPreviousOrder={isOwned}
|
||||||
hasDatatoken={hasDatatoken}
|
hasDatatoken={hasDatatoken}
|
||||||
|
btSymbol={asset?.accessDetails?.baseToken?.symbol}
|
||||||
dtSymbol={asset?.datatokens[0]?.symbol}
|
dtSymbol={asset?.datatokens[0]?.symbol}
|
||||||
dtBalance={dtBalance}
|
dtBalance={dtBalance}
|
||||||
onClick={handleOrderOrDownload}
|
onClick={handleOrderOrDownload}
|
||||||
|
@ -14,6 +14,7 @@ import { useIsMounted } from '@hooks/useIsMounted'
|
|||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { useFormikContext } from 'formik'
|
import { useFormikContext } from 'formik'
|
||||||
import { FormPublishData } from 'src/components/Publish/_types'
|
import { FormPublishData } from 'src/components/Publish/_types'
|
||||||
|
import { getTokenBalanceFromSymbol } from '@utils/web3'
|
||||||
import AssetStats from './AssetStats'
|
import AssetStats from './AssetStats'
|
||||||
|
|
||||||
export default function AssetActions({
|
export default function AssetActions({
|
||||||
@ -104,14 +105,19 @@ export default function AssetActions({
|
|||||||
if (asset?.accessDetails?.type === 'free') setIsBalanceSufficient(true)
|
if (asset?.accessDetails?.type === 'free') setIsBalanceSufficient(true)
|
||||||
if (
|
if (
|
||||||
!asset?.accessDetails?.price ||
|
!asset?.accessDetails?.price ||
|
||||||
|
!asset?.accessDetails?.baseToken?.symbol ||
|
||||||
!accountId ||
|
!accountId ||
|
||||||
!balance?.ocean ||
|
!balance ||
|
||||||
!dtBalance
|
!dtBalance
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
const baseTokenBalance = getTokenBalanceFromSymbol(
|
||||||
|
balance,
|
||||||
|
asset?.accessDetails?.baseToken?.symbol
|
||||||
|
)
|
||||||
setIsBalanceSufficient(
|
setIsBalanceSufficient(
|
||||||
compareAsBN(balance.ocean, `${asset?.accessDetails.price}`) ||
|
compareAsBN(baseTokenBalance, `${asset?.accessDetails.price}`) ||
|
||||||
Number(dtBalance) >= 1
|
Number(dtBalance) >= 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,3 +24,7 @@
|
|||||||
.add {
|
.add {
|
||||||
font-size: var(--font-size-mini);
|
font-size: var(--font-size-mini);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add svg path {
|
||||||
|
fill: var(--brand-violet);
|
||||||
|
}
|
||||||
|
@ -41,7 +41,6 @@ export default function MetaAsset({
|
|||||||
<AddToken
|
<AddToken
|
||||||
address={asset?.services[0].datatokenAddress}
|
address={asset?.services[0].datatokenAddress}
|
||||||
symbol={(asset as Asset)?.datatokens[0]?.symbol}
|
symbol={(asset as Asset)?.datatokens[0]?.symbol}
|
||||||
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/datatoken.png"
|
|
||||||
text={`Add ${(asset as Asset)?.datatokens[0]?.symbol} to wallet`}
|
text={`Add ${(asset as Asset)?.datatokens[0]?.symbol} to wallet`}
|
||||||
className={styles.add}
|
className={styles.add}
|
||||||
minimal
|
minimal
|
||||||
|
@ -88,3 +88,7 @@
|
|||||||
.addToken {
|
.addToken {
|
||||||
margin-left: 0.3rem;
|
margin-left: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addToken svg path {
|
||||||
|
fill: var(--brand-violet);
|
||||||
|
}
|
||||||
|
@ -48,7 +48,7 @@ export default function Details(): ReactElement {
|
|||||||
{Object.entries(balance).map(([key, value]) => (
|
{Object.entries(balance).map(([key, value]) => (
|
||||||
<li className={styles.balance} key={key}>
|
<li className={styles.balance} key={key}>
|
||||||
<span className={styles.symbol}>
|
<span className={styles.symbol}>
|
||||||
{key === 'eth' ? mainCurrency : oceanTokenMetadata?.symbol}
|
{key === 'eth' ? mainCurrency : key.toUpperCase()}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
{formatCurrency(Number(value), '', locale, false, {
|
{formatCurrency(Number(value), '', locale, false, {
|
||||||
significantFigures: 4
|
significantFigures: 4
|
||||||
@ -67,7 +67,6 @@ export default function Details(): ReactElement {
|
|||||||
<AddToken
|
<AddToken
|
||||||
address={oceanTokenMetadata?.address}
|
address={oceanTokenMetadata?.address}
|
||||||
symbol={oceanTokenMetadata?.symbol}
|
symbol={oceanTokenMetadata?.symbol}
|
||||||
logo="https://raw.githubusercontent.com/oceanprotocol/art/main/logo/token.png"
|
|
||||||
className={styles.addToken}
|
className={styles.addToken}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -20,8 +20,8 @@ export default function Preview(): ReactElement {
|
|||||||
price: `${values.pricing.price}`,
|
price: `${values.pricing.price}`,
|
||||||
baseToken: {
|
baseToken: {
|
||||||
address: ZERO_ADDRESS,
|
address: ZERO_ADDRESS,
|
||||||
name: 'OCEAN',
|
name: values.pricing?.baseToken?.symbol || 'OCEAN',
|
||||||
symbol: 'OCEAN'
|
symbol: values.pricing?.baseToken?.symbol || 'OCEAN'
|
||||||
},
|
},
|
||||||
datatoken: {
|
datatoken: {
|
||||||
address: ZERO_ADDRESS,
|
address: ZERO_ADDRESS,
|
||||||
|
22
src/components/Publish/Pricing/CoinSelect.module.css
Normal file
22
src/components/Publish/Pricing/CoinSelect.module.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.coinSelect {
|
||||||
|
composes: select from '../../@shared/FormInput/InputElement.module.css';
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
border: none;
|
||||||
|
margin-right: -0.5rem;
|
||||||
|
background-color: var(--background-highlight);
|
||||||
|
width: auto;
|
||||||
|
padding: 0 1.75rem 0 0.25rem;
|
||||||
|
height: 41px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
|
||||||
|
/* custom arrow, without the divider line */
|
||||||
|
background-image: linear-gradient(
|
||||||
|
45deg,
|
||||||
|
transparent 50%,
|
||||||
|
var(--font-color-text) 50%
|
||||||
|
),
|
||||||
|
linear-gradient(135deg, var(--font-color-text) 50%, transparent 50%);
|
||||||
|
background-position: calc(100% - 14px) 1.2rem, calc(100% - 9px) 1.2rem, 100% 0;
|
||||||
|
}
|
34
src/components/Publish/Pricing/CoinSelect.tsx
Normal file
34
src/components/Publish/Pricing/CoinSelect.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import Input from '@shared/FormInput'
|
||||||
|
import InputElement from '@shared/FormInput/InputElement'
|
||||||
|
import { useFormikContext } from 'formik'
|
||||||
|
import React, { ChangeEvent, ReactElement } from 'react'
|
||||||
|
import { FormPublishData } from '../_types'
|
||||||
|
import styles from './CoinSelect.module.css'
|
||||||
|
|
||||||
|
export default function CoinSelect({
|
||||||
|
approvedBaseTokens
|
||||||
|
}: {
|
||||||
|
approvedBaseTokens: TokenInfo[]
|
||||||
|
}): ReactElement {
|
||||||
|
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
||||||
|
|
||||||
|
const handleBaseTokenSelection = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
const selectedBaseToken = approvedBaseTokens.find(
|
||||||
|
(token) => token.symbol === e.target.value
|
||||||
|
)
|
||||||
|
setFieldValue('pricing.baseToken', selectedBaseToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
approvedBaseTokens?.length > 0 && (
|
||||||
|
<InputElement
|
||||||
|
name="coinselect"
|
||||||
|
className={styles.coinSelect}
|
||||||
|
type="select"
|
||||||
|
options={approvedBaseTokens?.map((token) => token.symbol)}
|
||||||
|
value={values.pricing?.baseToken?.symbol}
|
||||||
|
onChange={handleBaseTokenSelection}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
32
src/components/Publish/Pricing/Fixed.module.css
Normal file
32
src/components/Publish/Pricing/Fixed.module.css
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.title {
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: calc(var(--spacer) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tokens {
|
||||||
|
display: grid;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
background: var(--background-highlight);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 40rem) {
|
||||||
|
.tokens {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--color-secondary);
|
||||||
|
margin-left: calc(var(--spacer) / 4);
|
||||||
|
font-family: var(--font-family-base);
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alertArea {
|
||||||
|
padding: 0 calc(var(--spacer) / 2);
|
||||||
|
}
|
@ -2,16 +2,22 @@ import React, { ReactElement } from 'react'
|
|||||||
import FormHelp from '@shared/FormInput/Help'
|
import FormHelp from '@shared/FormInput/Help'
|
||||||
import Price from './Price'
|
import Price from './Price'
|
||||||
import Fees from './Fees'
|
import Fees from './Fees'
|
||||||
import styles from './index.module.css'
|
import styles from './Fixed.module.css'
|
||||||
|
|
||||||
export default function Fixed({ content }: { content: any }): ReactElement {
|
export default function Fixed({
|
||||||
|
approvedBaseTokens,
|
||||||
|
content
|
||||||
|
}: {
|
||||||
|
approvedBaseTokens: TokenInfo[]
|
||||||
|
content: any
|
||||||
|
}): ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormHelp>{content.info}</FormHelp>
|
<FormHelp>{content.info}</FormHelp>
|
||||||
|
|
||||||
<h4 className={styles.title}>Price</h4>
|
<h4 className={styles.title}>Price</h4>
|
||||||
|
|
||||||
<Price />
|
<Price approvedBaseTokens={approvedBaseTokens} />
|
||||||
<Fees tooltips={content.tooltips} />
|
<Fees tooltips={content.tooltips} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -1,34 +1,43 @@
|
|||||||
|
.form {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form *,
|
||||||
|
.form label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.price {
|
.price {
|
||||||
background: var(--background-highlight);
|
background: var(--background-highlight);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
padding: calc(var(--spacer) / 1.5) calc(var(--spacer) / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form {
|
.grid {
|
||||||
display: flex;
|
display: grid;
|
||||||
justify-content: center;
|
gap: calc(var(--spacer) / 2);
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
|
||||||
|
padding: calc(var(--spacer) / 2);
|
||||||
|
max-width: 30rem;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputWrap {
|
|
||||||
position: relative;
|
|
||||||
max-width: 15rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputWrap > div {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fixed label {
|
.fixed label {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.datatoken {
|
.datatoken {
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.datatoken h4 {
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-base);
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-left: calc(var(--spacer) / 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.datatoken strong {
|
.datatoken strong {
|
||||||
@ -41,10 +50,10 @@
|
|||||||
|
|
||||||
.free {
|
.free {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
margin: calc(var(--spacer) / 2);
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.free [class*='FormInput_field'],
|
.free [class*='FormInput_field'] {
|
||||||
.free [class*='InputRadio_radioGroup'] {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -6,43 +6,57 @@ import Error from '@shared/FormInput/Error'
|
|||||||
import styles from './Price.module.css'
|
import styles from './Price.module.css'
|
||||||
import { FormPublishData } from '../_types'
|
import { FormPublishData } from '../_types'
|
||||||
import { getFieldContent } from '@utils/form'
|
import { getFieldContent } from '@utils/form'
|
||||||
|
import CoinSelect from './CoinSelect'
|
||||||
|
|
||||||
export default function Price({ content }: { content?: any }): ReactElement {
|
export default function Price({
|
||||||
|
approvedBaseTokens,
|
||||||
|
content
|
||||||
|
}: {
|
||||||
|
approvedBaseTokens?: TokenInfo[]
|
||||||
|
content?: any
|
||||||
|
}): ReactElement {
|
||||||
const [field, meta] = useField('pricing.price')
|
const [field, meta] = useField('pricing.price')
|
||||||
|
|
||||||
const { values } = useFormikContext<FormPublishData>()
|
const { values } = useFormikContext<FormPublishData>()
|
||||||
const { dataTokenOptions } = values.services[0]
|
const { dataTokenOptions } = values.services[0]
|
||||||
|
|
||||||
const classNames = `${styles.price} ${
|
|
||||||
values.pricing.type === 'free' ? styles.free : styles.fixed
|
|
||||||
}`
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames}>
|
<div className={styles.price}>
|
||||||
{values.pricing.type === 'free' ? (
|
{values.pricing.type === 'free' ? (
|
||||||
|
<div className={styles.free}>
|
||||||
<Field
|
<Field
|
||||||
{...getFieldContent('freeAgreement', content.fields)}
|
{...getFieldContent('freeAgreement', content.fields)}
|
||||||
component={Input}
|
component={Input}
|
||||||
name="pricing.freeAgreement"
|
name="pricing.freeAgreement"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
|
<div className={styles.grid}>
|
||||||
<div className={styles.form}>
|
<div className={styles.form}>
|
||||||
<div className={styles.inputWrap}>
|
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
prefix="OCEAN"
|
prefix={
|
||||||
|
approvedBaseTokens?.length > 1 ? (
|
||||||
|
<CoinSelect approvedBaseTokens={approvedBaseTokens} />
|
||||||
|
) : (
|
||||||
|
values.pricing?.baseToken?.symbol
|
||||||
|
)
|
||||||
|
}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
<Error meta={meta} />
|
<Error meta={meta} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className={styles.datatoken}>
|
||||||
<h4 className={styles.datatoken}>
|
<h4>
|
||||||
= <strong>1</strong> {dataTokenOptions.symbol}{' '}
|
= <strong>1</strong> {dataTokenOptions.symbol}{' '}
|
||||||
<Conversion price={field.value} className={styles.conversion} />
|
<Conversion price={field.value} className={styles.conversion} />
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { ReactElement, useEffect } from 'react'
|
import React, { ReactElement, useCallback, useEffect, useState } from 'react'
|
||||||
import { useFormikContext } from 'formik'
|
import { useFormikContext } from 'formik'
|
||||||
import Tabs from '@shared/atoms/Tabs'
|
import Tabs from '@shared/atoms/Tabs'
|
||||||
import { FormPublishData } from '../_types'
|
import { FormPublishData } from '../_types'
|
||||||
@ -7,14 +7,37 @@ import Free from './Free'
|
|||||||
import content from '../../../../content/price.json'
|
import content from '../../../../content/price.json'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { useMarketMetadata } from '@context/MarketMetadata'
|
import { useMarketMetadata } from '@context/MarketMetadata'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
|
||||||
export default function PricingFields(): ReactElement {
|
export default function PricingFields(): ReactElement {
|
||||||
const { appConfig } = useMarketMetadata()
|
const { appConfig } = useMarketMetadata()
|
||||||
|
const { approvedBaseTokens, chainId } = useWeb3()
|
||||||
// Connect with main publish form
|
// Connect with main publish form
|
||||||
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
||||||
const { pricing } = values
|
const { pricing } = values
|
||||||
const { price, type } = pricing
|
const { type } = pricing
|
||||||
|
|
||||||
|
const defaultBaseToken =
|
||||||
|
approvedBaseTokens?.find((token) =>
|
||||||
|
token.name.toLowerCase().includes('ocean')
|
||||||
|
) || approvedBaseTokens?.[0]
|
||||||
|
|
||||||
|
const isBaseTokenSet = !!approvedBaseTokens?.find(
|
||||||
|
(token) => token?.address === values?.pricing?.baseToken?.address
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!approvedBaseTokens?.length) return
|
||||||
|
if (isBaseTokenSet) return
|
||||||
|
setFieldValue('pricing.baseToken', defaultBaseToken)
|
||||||
|
}, [
|
||||||
|
approvedBaseTokens,
|
||||||
|
chainId,
|
||||||
|
defaultBaseToken,
|
||||||
|
isBaseTokenSet,
|
||||||
|
setFieldValue,
|
||||||
|
values.pricing.baseToken
|
||||||
|
])
|
||||||
|
|
||||||
// Switch type value upon tab change
|
// Switch type value upon tab change
|
||||||
function handleTabChange(tabName: string) {
|
function handleTabChange(tabName: string) {
|
||||||
@ -22,21 +45,24 @@ export default function PricingFields(): ReactElement {
|
|||||||
setFieldValue('pricing.type', type)
|
setFieldValue('pricing.type', type)
|
||||||
setFieldValue('pricing.price', 0)
|
setFieldValue('pricing.price', 0)
|
||||||
setFieldValue('pricing.freeAgreement', false)
|
setFieldValue('pricing.freeAgreement', false)
|
||||||
|
setFieldValue('pricing.baseToken', defaultBaseToken)
|
||||||
|
type !== 'free' && setFieldValue('pricing.amountDataToken', 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update price when price is changed
|
const updateTabs = useCallback(() => {
|
||||||
useEffect(() => {
|
return [
|
||||||
setFieldValue('pricing.price', price)
|
|
||||||
setFieldValue('pricing.type', type)
|
|
||||||
}, [price, setFieldValue, type])
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
appConfig.allowFixedPricing === 'true'
|
appConfig.allowFixedPricing === 'true'
|
||||||
? {
|
? {
|
||||||
title: content.create.fixed.title,
|
title: content.create.fixed.title,
|
||||||
content: <Fixed content={content.create.fixed} />
|
content: (
|
||||||
|
<Fixed
|
||||||
|
approvedBaseTokens={approvedBaseTokens}
|
||||||
|
content={content.create.fixed}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|
||||||
appConfig.allowFreePricing === 'true'
|
appConfig.allowFreePricing === 'true'
|
||||||
? {
|
? {
|
||||||
title: content.create.free.title,
|
title: content.create.free.title,
|
||||||
@ -44,6 +70,17 @@ export default function PricingFields(): ReactElement {
|
|||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
].filter((tab) => tab !== undefined)
|
].filter((tab) => tab !== undefined)
|
||||||
|
}, [
|
||||||
|
appConfig.allowFixedPricing,
|
||||||
|
appConfig.allowFreePricing,
|
||||||
|
approvedBaseTokens
|
||||||
|
])
|
||||||
|
|
||||||
|
const [tabs, setTabs] = useState(updateTabs())
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTabs(updateTabs())
|
||||||
|
}, [updateTabs])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
|
@ -22,6 +22,13 @@ export function Steps({
|
|||||||
setFieldValue('user.accountId', accountId)
|
setFieldValue('user.accountId', accountId)
|
||||||
}, [chainId, accountId, setFieldValue])
|
}, [chainId, accountId, setFieldValue])
|
||||||
|
|
||||||
|
// Reset the selected baseToken on chainId change
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chainId) return
|
||||||
|
|
||||||
|
setFieldValue('pricing.baseToken', null)
|
||||||
|
}, [chainId, setFieldValue])
|
||||||
|
|
||||||
// auto-sync publish feedback into form data values
|
// auto-sync publish feedback into form data values
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFieldValue('feedback', feedback)
|
setFieldValue('feedback', feedback)
|
||||||
|
@ -86,6 +86,7 @@ export const initialValues: FormPublishData = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
pricing: {
|
pricing: {
|
||||||
|
baseToken: { address: '', name: '', symbol: 'OCEAN', decimals: 18 },
|
||||||
price: 0,
|
price: 0,
|
||||||
type: allowFixedPricing === 'true' ? 'fixed' : 'free',
|
type: allowFixedPricing === 'true' ? 'fixed' : 'free',
|
||||||
freeAgreement: false
|
freeAgreement: false
|
||||||
|
@ -207,7 +207,7 @@ export async function createTokensAndPricing(
|
|||||||
minter: accountId,
|
minter: accountId,
|
||||||
paymentCollector: accountId,
|
paymentCollector: accountId,
|
||||||
mpFeeAddress: marketFeeAddress,
|
mpFeeAddress: marketFeeAddress,
|
||||||
feeToken: config.oceanTokenAddress,
|
feeToken: values.pricing.baseToken.address,
|
||||||
feeAmount: publisherMarketOrderFee,
|
feeAmount: publisherMarketOrderFee,
|
||||||
// max number
|
// max number
|
||||||
cap: '115792089237316195423570985008687907853269984665640564039457',
|
cap: '115792089237316195423570985008687907853269984665640564039457',
|
||||||
@ -223,10 +223,10 @@ export async function createTokensAndPricing(
|
|||||||
case 'fixed': {
|
case 'fixed': {
|
||||||
const freParams: FreCreationParams = {
|
const freParams: FreCreationParams = {
|
||||||
fixedRateAddress: config.fixedRateExchangeAddress,
|
fixedRateAddress: config.fixedRateExchangeAddress,
|
||||||
baseTokenAddress: config.oceanTokenAddress,
|
baseTokenAddress: values.pricing.baseToken.address,
|
||||||
owner: accountId,
|
owner: accountId,
|
||||||
marketFeeCollector: marketFeeAddress,
|
marketFeeCollector: marketFeeAddress,
|
||||||
baseTokenDecimals: 18,
|
baseTokenDecimals: values.pricing.baseToken.decimals,
|
||||||
datatokenDecimals: 18,
|
datatokenDecimals: 18,
|
||||||
fixedRate: values.pricing.price.toString(),
|
fixedRate: values.pricing.price.toString(),
|
||||||
marketFee: publisherMarketFixedSwapFee,
|
marketFee: publisherMarketFixedSwapFee,
|
||||||
|
Loading…
Reference in New Issue
Block a user