Skip to content

Commit

Permalink
fix: go to service Button
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinRohlf authored and moritzkirstein committed Nov 25, 2024
1 parent 2918955 commit 0977141
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 11 deletions.
5 changes: 5 additions & 0 deletions app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ module.exports = {
// Display alert banner for the developer preview deployment
showPreviewAlert: process.env.NEXT_PUBLIC_SHOW_PREVIEW_ALERT || 'false',

contractingProvider: {
enable: true,
endpoint: 'https://contracting.demo.delta-dao.com'
},

networkAlertConfig: {
// Refresh interval for network status - 30 sec
refreshInterval: 30000,
Expand Down
4 changes: 4 additions & 0 deletions src/@context/MarketMetadata/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export interface AppConfig {
roughTxGasEstimate: number
}
showPreviewAlert: string
contractingProvider: {
enable: boolean
endpoint: string
}
networkAlertConfig: {
// Refresh interval for network status - 30 sec
refreshInterval: number
Expand Down
15 changes: 12 additions & 3 deletions src/components/Asset/AssetActions/ButtonBuy/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { FormEvent, ReactElement } from 'react'
import Button from '../../../@shared/atoms/Button'
import styles from './index.module.css'
import Loader from '../../../@shared/atoms/Loader'
import { PAYMENT_MODES, PaymentMode } from '../Download/ContractingProvider'
import styles from './index.module.css'

export interface ButtonBuyProps {
action: 'download' | 'compute'
Expand Down Expand Up @@ -32,6 +33,7 @@ export interface ButtonBuyProps {
isAccountConnected?: boolean
hasProviderFee?: boolean
retry?: boolean
paymentMode?: PaymentMode
}

function getConsumeHelpText(
Expand Down Expand Up @@ -166,6 +168,7 @@ export default function ButtonBuy({
dtSymbol,
dtBalance,
assetType,
paymentMode,
assetTimeout,
isConsumable,
consumableFeedback,
Expand All @@ -191,13 +194,19 @@ export default function ButtonBuy({
? 'Retry'
: action === 'download'
? hasPreviousOrder && assetType === 'saas'
? 'Go to service'
? paymentMode === PAYMENT_MODES.PAYPERUSE
? `Buy access credit`
: 'Go to service'
: hasPreviousOrder
? 'Download'
: priceType === 'free'
? 'Get'
: assetType === 'saas'
? `Subscribe ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
? paymentMode === PAYMENT_MODES.PAYPERUSE
? `Buy access credit`
: `Subscribe ${
assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`
}`
: `Buy ${assetTimeout === 'Forever' ? '' : ` for ${assetTimeout}`}`
: hasPreviousOrder &&
hasPreviousOrderSelectedComputeAsset &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.container {
display: flex;
flex-direction: column;
margin-top: calc(var(--spacer) / 1.5);
margin-bottom: -1rem;
margin-left: calc(var(--spacer) / -1.5);
margin-right: calc(var(--spacer) / -1.5);
padding: calc(var(--spacer) / 1.5);
border-top: 1px solid var(--border-color);
}

.help {
composes: help from '../../ButtonBuy/index.module.css';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { ReactElement, useCallback, useEffect, useState } from 'react'
import styles from './index.module.css'
import Button from '../../../../@shared/atoms/Button'
import { useAccount, useSignMessage } from 'wagmi'
import { getContractingProviderNonce, getPayPerUseCount } from '../../utils'
import Alert from '../../../../@shared/atoms/Alert'
import { useMarketMetadata } from '../../../../../@context/MarketMetadata'
import { useAutomation } from '../../../../../@context/Automation/AutomationProvider'

export enum PAYMENT_MODES {
SUBSCRIPTION = 'subscription',
PAYPERUSE = 'payperuse'
}

export type PaymentMode = `${PAYMENT_MODES}`

export default function ContractingProvider(props: {
did: string
}): ReactElement {
const { did } = props
const { address } = useAccount()
const [isRequesting, setIsRequesting] = useState(false)
const [accessCreditsCount, setAccessCreditsCount] = useState<number>()
const {
signMessage,
data: signMessageData,
isSuccess,
isError
} = useSignMessage()
const {
appConfig: {
contractingProvider: { endpoint: contractingProviderEndpoint }
}
} = useMarketMetadata()

const { autoWallet, isAutomationEnabled } = useAutomation()

const [activeAddress, setActiveAddress] = useState<string>()
const [signature, setSignature] = useState<string>()

useEffect(() => {
if (isAutomationEnabled) setActiveAddress(autoWallet.address)
else setActiveAddress(address)
}, [address, autoWallet?.address, isAutomationEnabled])

const checkAccessCredits = async () => {
setIsRequesting(true)

const nonce = await getContractingProviderNonce(
contractingProviderEndpoint,
activeAddress
)
if (isAutomationEnabled) {
try {
const autoWalletSignature = await autoWallet.signMessage(nonce)
setSignature(autoWalletSignature)
} catch (e) {
setIsRequesting(false)
console.error(e)
}
} else {
signMessage({ message: nonce })
}
}

const updateCount = useCallback(async () => {
const count = await getPayPerUseCount(
contractingProviderEndpoint,
activeAddress,
signature,
did
)
setAccessCreditsCount(count)
setIsRequesting(false)
}, [contractingProviderEndpoint, activeAddress, signature, did])

useEffect(() => {
if (isError) setIsRequesting(false)
if (isSuccess) {
setSignature(signMessageData)
}
}, [isSuccess, isError])

useEffect(() => {
if (!signature) return
updateCount()
}, [signature])

return (
<div className={styles.container}>
{accessCreditsCount ? (
<Alert
state="info"
text={`You purchased access to this service **${accessCreditsCount} time${
accessCreditsCount > 1 ? 's' : ''
}**`}
action={{
name: 'Re-run',
handleAction: (e) => {
setAccessCreditsCount(undefined) // force visible re-render
e.preventDefault()
checkAccessCredits()
}
}}
/>
) : (
<Button
style="text"
onClick={(e) => {
e.preventDefault()
checkAccessCredits()
}}
disabled={isRequesting}
>
Check Access Credits
</Button>
)}
<div className={styles.help}>
You can validate your purchase count of this SaaS Offering against a
contracting provider instance.
</div>
</div>
)
}
64 changes: 56 additions & 8 deletions src/components/Asset/AssetActions/Download/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import WhitelistIndicator from '../Compute/WhitelistIndicator'
import { Signer } from 'ethers'
import SuccessConfetti from '@components/@shared/SuccessConfetti'
import Input from '@components/@shared/FormInput'
import ContractingProvider, { PAYMENT_MODES } from './ContractingProvider'
import Button from '@components/@shared/atoms/Button'

export default function Download({
accountId,
Expand All @@ -61,7 +63,12 @@ export default function Download({
}): ReactElement {
const { isConnected } = useAccount()
const { isSupportedOceanNetwork } = useNetworkMetadata()
const { getOpcFeeForToken } = useMarketMetadata()
const {
getOpcFeeForToken,
appConfig: {
contractingProvider: { enable: isContractingFeatureEnabled }
}
} = useMarketMetadata()
const { isInPurgatory, isAssetNetwork } = useAsset()
const isMounted = useIsMounted()

Expand Down Expand Up @@ -171,16 +178,21 @@ export default function Download({
isAccountIdWhitelisted
])

function redirectToSaasUrl() {
window.open(asset.metadata.additionalInformation.saas.redirectUrl, '_blank')
}

async function handleOrderOrDownload(dataParams?: UserCustomParameters) {
setIsLoading(true)
setRetry(false)
try {
if (isOwned) {
if (
isOwned &&
asset?.metadata?.additionalInformation?.saas?.paymentMode !==
PAYMENT_MODES.PAYPERUSE
) {
if (asset?.metadata?.additionalInformation?.saas?.redirectUrl) {
window.open(
asset.metadata.additionalInformation.saas.redirectUrl,
'_blank'
)
redirectToSaasUrl()
setIsLoading(false)
return
}
Expand Down Expand Up @@ -236,12 +248,22 @@ export default function Download({
dtSymbol={asset?.datatokens[0]?.symbol}
dtBalance={dtBalance}
type="submit"
assetTimeout={secondsToString(asset?.services?.[0]?.timeout)}
assetTimeout={
asset?.metadata?.additionalInformation?.saas?.paymentMode ===
PAYMENT_MODES.PAYPERUSE
? // we dont have a timeout on payperuse
// as this is handled by service operators utilizing contracting provider
secondsToString(0)
: secondsToString(asset?.services?.[0]?.timeout)
}
assetType={
asset?.metadata?.additionalInformation?.saas
? 'saas'
: asset?.metadata?.type
}
paymentMode={
asset?.metadata?.additionalInformation?.saas?.paymentMode ?? undefined
}
stepText={statusText}
isLoading={isLoading}
priceType={asset.accessDetails?.type}
Expand Down Expand Up @@ -284,7 +306,28 @@ export default function Download({
size="large"
/>
)}
{!isInPurgatory && <PurchaseButton isValid={isValid} />}
{!isInPurgatory && (
<>
{asset?.metadata?.additionalInformation?.saas
?.paymentMode === PAYMENT_MODES.PAYPERUSE &&
asset?.metadata?.additionalInformation?.saas
?.redirectUrl && (
<div className={styles.payPerUseBtn}>
<Button
style="primary"
onClick={(e) => {
e.preventDefault()
redirectToSaasUrl()
}}
disabled={!isValid}
>
Go to service
</Button>
</div>
)}
<PurchaseButton isValid={isValid} />
</>
)}
<Field
component={Input}
name="termsAndConditions"
Expand Down Expand Up @@ -360,6 +403,11 @@ export default function Download({
asset={asset}
/>
)}
{isContractingFeatureEnabled &&
asset?.metadata?.additionalInformation?.saas?.paymentMode ===
PAYMENT_MODES.PAYPERUSE &&
accountId &&
isAccountIdWhitelisted && <ContractingProvider did={asset.id} />}
{accountId && (
<WhitelistIndicator
accountId={accountId}
Expand Down
44 changes: 44 additions & 0 deletions src/components/Asset/AssetActions/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import axios from 'axios'
import { toast } from 'react-toastify'

export async function getContractingProviderNonce(
contractingBaseUrl: string,
address: string
) {
try {
const response = await axios.get(
`${contractingBaseUrl}/user/${address}/nonce`
)
return response.data
} catch (e) {
console.error(e)
toast.error('Could not get nonce from contracting provider.')
}
}

export async function getPayPerUseCount(
contractingBaseUrl: string,
address: string,
signature: string,
did: string
) {
try {
const response = await axios.post(
`${contractingBaseUrl}/contracting/validate`,
{ address, signature, did },
{
// throw only on status codes we dont expect
// 404 returned if no transaction was found for address & did combination
// 401 returned if 'hasAccess' for address on did is = false
validateStatus: (status) => status < 400 || [404, 401].includes(status)
}
)

const { data } = response

return data?.orderCount
} catch (e) {
console.error(e)
toast.error('Could not retrieve information from contracting provider.')
}
}

0 comments on commit 0977141

Please sign in to comment.