Skip to content

oceanprotocol/ocean.js-react-native-wrapper

Repository files navigation

Ocean.js React Native Demo app

Demo React Native app using ocean.js to buy assets

Prerequisites

  • node.js (install from here)
  • phone (Android or iOS) with Expo Go installed
  • WalletConnect project (create an account & project from here)
  • Magic Link project (create an account & project from here)

Installation

  1. Install dependencies
$ npm install
  1. Create the .env file from the env example file
$ cp .env.example .env
  1. Add the necessary env variables

Usage

  1. Run the app
$ npm start
  1. Install the Expo app on your phone and scan the QR code

Note: You need to be on the same network in order to connect to the app. If you cannot connect due to some network conditions/firewall rules you can run the app with tunneling:

$ npm start -- --tunnel

How to Build a React Native Dapp with Ocean.js

The following guide runs you through the process of building a simple React Native dapp that connects a wallet with WalletConnect or Magic Link and uses the Ocean.js library to buy assets.

Contents

Prerequisites

Create a new Expo project with Typescript

$ npx create-expo-app <your_project_name> -t expo-template-blank-typescript
$ cd <your_project_name>

Connect a wallet

Option 1: WalletConnect

  1. Register on WalletConnect and create a project. We'll need the Project ID later.

  2. Install dependencies:

$ npx expo install @walletconnect/modal-react-native
$ npx expo install @react-native-async-storage/async-storage react-native-modal react-native-svg

Because of this bug we need to add the following in package.json:

"expo": {
    "install": {
      "exclude": [
        "react-native-get-random-values"
      ]
    }
},

and install react-native-get-random-values:

$ npm install react-native-get-random-values
  1. Create a .env file and add the following:
EXPO_PUBLIC_CHAIN_ID=
EXPO_PUBLIC_RPC_URL=
EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID=
  1. Add a Home screen

Create a simple file Home.tsx in src/screens/ and add the following:

import { StyleSheet, Text, View } from 'react-native';

export default function Home() {
  return (
    <View style={styles.container}>
      <Text>Hello World</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Export the Home screen in App.tsx:

export { default } from './src/screens/Home';
  1. Add WalletConnectModal

Open Home.tsx and render WalletConnectModal. Add your project's info in providerMetadata.

import { IProviderMetadata, WalletConnectModal } from '@walletconnect/modal-react-native';
import { ISessionParams } from '@walletconnect/modal-react-native/lib/typescript/src/types/coreTypes';
import { SafeAreaView } from 'react-native';

const chainId = parseInt(process.env.EXPO_PUBLIC_CHAIN_ID || '', 10);
const rpcUrl = process.env.EXPO_PUBLIC_RPC_URL || '';
const projectId = process.env.EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID || '';

const providerMetadata: IProviderMetadata = {
  name: 'YOUR_PROJECT_NAME',
  description: 'YOUR_PROJECT_DESCRIPTION',
  url: 'https://your-project-website.com/',
  icons: ['https://your-project-logo.com/'],
  redirect: {
    native: 'YOUR_APP_SCHEME://',
    universal: 'YOUR_APP_UNIVERSAL_LINK.com',
  },
};

export const sessionParams: ISessionParams = {
  namespaces: {
    eip155: {
      methods: ['eth_sendTransaction', 'eth_signTransaction', 'eth_sign', 'personal_sign', 'eth_signTypedData'],
      chains: [`eip155:${chainId}`],
      events: ['chainChanged', 'accountsChanged'],
      rpcMap: {
        [chainId]: rpcUrl,
      },
    },
  },
};

export default function Home() {
  return (
    <SafeAreaView>
      <WalletConnectModal projectId={projectId} providerMetadata={providerMetadata} sessionParams={sessionParams} />
    </SafeAreaView>
  );
}
  1. Add the Connect & Disconnect buttons

We start by calling useWalletConnectModal hook to get the necessary props:

const { isConnected, open, provider } = useWalletConnectModal();

Add a callback for the connect action:

const onPressConnect = useCallback(async () => {
    return open();
}, []);

Add a callback for the disconnect action:

const onPressDisconnect = useCallback(async () => {
    return provider?.disconnect();
}, [provider]);

Add some simple styles:

const styles = StyleSheet.create({
    container: {
        alignItems: 'center',
        justifyContent: 'center',
        flex: 1,
    },
    button: {
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#3396FF',
        borderRadius: 20,
        width: 200,
        height: 50,
        borderWidth: 1,
        borderColor: 'rgba(0, 0, 0, 0.1)',
    },
    dangerButton: {
        backgroundColor: 'red',
    },
    text: {
        color: 'white',
        fontWeight: '700',
    },
});

Add the Connect & Disconnect buttons:

return (
    <SafeAreaView style={styles.container}>
        {isConnected ? (
            <TouchableOpacity style={[styles.button, styles.dangerButton]} onPress={onPressDisconnect}>
                <Text style={styles.text}>Disconnect</Text>
            </TouchableOpacity>
        ) : (
            <TouchableOpacity style={styles.button} onPress={onPressConnect}>
                <Text style={styles.text}>Connect</Text>
            </TouchableOpacity>
        )}
        <WalletConnectModal projectId={projectId} providerMetadata={providerMetadata} sessionParams={sessionParams} />
    </SafeAreaView>
);

Option 2: Magic Link

  1. Register on Magic Link and create a project. We'll need the API key later.

  2. Install dependencies:

$ npm install @magic-sdk/react-native-expo
$ npm install react-native-webview react-native-safe-area-context @react-native-async-storage/async-storage # Required peer dependencies
  1. Create a .env file and add the following:
EXPO_PUBLIC_CHAIN_ID=
EXPO_PUBLIC_RPC_URL=
EXPO_PUBLIC_MAGIC_API_KEY=
  1. Add a file magic.ts and create an Magic Link SDK instance:
import { Magic } from '@magic-sdk/react-native-expo';

const chainId = parseInt(process.env.EXPO_PUBLIC_CHAIN_ID || '', 10);
const rpcUrl = process.env.EXPO_PUBLIC_RPC_URL || '';
const apiKey = process.env.EXPO_PUBLIC_MAGIC_API_KEY || '';

const customNodeOptions = {
  rpcUrl: rpcUrl,
  chainId: chainId,
};

const magic = new Magic(apiKey, {
  network: customNodeOptions,
});

export default magic;
  1. Render the Magic component

Open App.tsx and add the magic.Relayer component wrapped in SafeAreaProvider:

import { SafeAreaProvider } from 'react-native-safe-area-context';

import magic from './magic';

export default function App() {
  return (
    <SafeAreaProvider>
      <magic.Relayer />
    </SafeAreaProvider>
  );
}
  1. Add a Home screen

Create a simple file Home.tsx in src/screens/ and add the following:

import { StyleSheet, Text, View } from 'react-native';

export default function Home() {
  return (
    <View style={styles.container}>
      <Text>Hello World</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Render the Home screen in App.tsx AFTER magic.Relayer:

return (
    <SafeAreaProvider>
        <magic.Relayer />
        <Home />
    </SafeAreaProvider>
);
  1. Add the Connect & Disconnect buttons

We are going to use a state to know if the wallet is connected or not. We use another state for the Magic Link provider (it will be used later). Open Home.tsx and add the following:

const [isConnected, setIsConnected] = useState(false);
const [provider, setProvider] = useState();

Add a callback to the connect action:

const onPressConnect = useCallback(async () => {
    await magic.wallet.connectWithUI();
    const provider = await magic.wallet.getProvider();
    setProvider(provider);
    setIsConnected(true);
}, []);

Add a callback for the disconnect action:

const onPressDisconnect = useCallback(async () => {
    await magic.user.logout();
    setProvider(undefined);
    setIsConnected(false);
}, []);

Add some simple styles:

const styles = StyleSheet.create({
    container: {
        alignItems: 'center',
        justifyContent: 'center',
        flex: 1,
    },
    button: {
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#3396FF',
        borderRadius: 20,
        width: 200,
        height: 50,
        borderWidth: 1,
        borderColor: 'rgba(0, 0, 0, 0.1)',
        marginTop: 4,
    },
    dangerButton: {
        backgroundColor: 'red',
    },
    text: {
        color: 'white',
        fontWeight: '700',
    },
});

Add the Connect & Disconnect buttons:

return (
    <SafeAreaView style={styles.container}>
        {isConnected ? (
            <TouchableOpacity style={[styles.button, styles.dangerButton]} onPress={onPressDisconnect}>
                <Text style={styles.text}>Disconnect</Text>
            </TouchableOpacity>
        ) : (
            <TouchableOpacity style={styles.button} onPress={onPressConnect}>
                <Text style={styles.text}>Connect</Text>
            </TouchableOpacity>
        )}
    </SafeAreaView>
);

Additionally, when the app loads we need to check if the user is already logged in:

useEffect(() => {
    const init = async () => {
        const [isLoggedIn, providerResult] = await Promise.all([magic.user.isLoggedIn(), magic.wallet.getProvider()]);
        setIsConnected(isLoggedIn);
        setProvider(providerResult);
    };

    init();
}, []);
  1. Add the Manage wallet button

When you log in with Magic Link by email a wallet address is generated automatically for you. In order to do any blockchain transaction later you need some tokens in order to pay the gas fees. Magic Link provides a widget to manage your wallet address (receive & send tokens, buy tokens with debit card, etc.) and we need to add a button to show it.

Create a callback for the manage wallet action:

const onPressManageWallet = useCallback(async () => {
    await magic.wallet.showUI();
}, []);

Add the Manage wallet button:

{isConnected ? (
    <View>
        <TouchableOpacity style={[styles.button]} onPress={onPressManageWallet}>
            <Text style={styles.text}>Manage wallet</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.button, styles.dangerButton]} onPress={onPressDisconnect}>
            <Text style={styles.text}>Disconnect</Text>
        </TouchableOpacity>
    </View>
) : (
    <TouchableOpacity style={styles.button} onPress={onPressConnect}>
        <Text style={styles.text}>Connect</Text>
    </TouchableOpacity>
)}

Create a Web3 provider

This Web3 provider will be used for all blockchain actions.

Install ethers.js v5 (v6 is not supported by the Ocean.js library):

$ npm install ethers@^5.7.2

Open Home.tsx and create a Web3 provider:

const web3Provider = useMemo(() => (provider ? new ethers.providers.Web3Provider(provider) : undefined), [provider]);

Add Ocean.js

  1. Install library:
$ npm install @oceanprotocol/lib
  1. Add an env var for the asset DID:
EXPO_PUBLIC_OCEAN_DID=
  1. Create a file ocean.ts and add the following:
import { Aquarius, Config, ConfigHelper } from '@oceanprotocol/lib';

export const oceanConfig: Config = new ConfigHelper().getConfig(parseInt(process.env.EXPO_PUBLIC_CHAIN_ID || '', 10));

export const aquarius = new Aquarius(oceanConfig.metadataCacheUri!);

export const ASSET_DID = process.env.EXPO_PUBLIC_OCEAN_DID || '';

This is the config file for Ocean.js. We'll use it to load & buy assets.

Buy an asset

Buying an asset involves a two-step process: obtaining a datatoken (by buying or receiving one for free) and send it to the publisher to place the order for that asset (more info about this here).

Preparation

We are going to add a component that loads an asset with a button to purchase it.

  1. Create a simple file BuyAsset.tsx in src/components/ and add the following:
import { StyleSheet, Text, View } from 'react-native';
import { ethers } from 'ethers';

type BuyAssetProps = {
  web3Provider: ethers.providers.Web3Provider;
};

export default function BuyAsset({ web3Provider }: BuyAssetProps) {
  return (
    <View style={styles.container}>
      <Text>Buy asset</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    marginTop: 36,
  },
});

Render the new component in Home.tsx:

{isConnected ? (
    <View>
      <TouchableOpacity style={[styles.button, styles.dangerButton]} onPress={onPressDisconnect}>
        <Text style={styles.text}>Disconnect</Text>
      </TouchableOpacity>
      <BuyAsset web3Provider={web3Provider!} />
    </View>
) : (
    <TouchableOpacity style={styles.button} onPress={onPressConnect}>
      <Text style={styles.text}>Connect</Text>
    </TouchableOpacity>
)}
  1. Load the asset

We are going to load the asset by using his DID (Decentralized identifier).

Open BuyAsset.tsx and add a state for the asset:

const [asset, setAsset] = useState<Asset>();

Add an useEffect to get the asset and save it:

useEffect(() => {
  aquarius.resolve(ASSET_DID).then((result) => setAsset(result));
}, []);

Each asset contains multiple datatokens & services. We are going to buy the first datatoken in the list.

Add a const for the datatoken info:

const dataToken = useMemo(() => {
  if (!asset) {
    return;
  }

  return asset.datatokens[0];
}, [asset]);

Add a const for the datatoken service:

const dataTokenService = useMemo(() => {
  if (!asset) {
    return;
  }

  return asset.services.find((current) => current.datatokenAddress === dataToken?.address);
}, [asset, dataToken?.address]);
  1. Add a buy button

Add a loading state to show feedback to the user while the buy action is in progress:

const [loading, setLoading] = useState(false);

Add a callback for the buy action. We'll add the code later for each usecase:

const onPressBuy = useCallback(() => {
  // buy code
}, []);

Add some simple styles:

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    marginTop: 36,
  },
  button: {
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#3396FF',
    borderRadius: 20,
    width: 200,
    height: 50,
    borderWidth: 1,
    borderColor: 'rgba(0, 0, 0, 0.1)',
    marginTop: 8,
  },
  buttonDisabled: {
    opacity: 0.6,
  },
  text: {
    color: 'white',
    fontWeight: '700',
  },
});

Add the buy button:

return (
  <View style={styles.container}>
    <Text>Buy asset</Text>
    {dataToken && (
      <TouchableOpacity
        style={[styles.button, loading ? styles.buttonDisabled : undefined]}
        onPress={onPressBuy}
        disabled={loading}
      >
        {loading ? <ActivityIndicator color="white" /> : <Text style={styles.text}>{dataToken.name}</Text>}
      </TouchableOpacity>
    )}
  </View>
);

Asset types

There are 2 pricing schema for an asset: fixed-rate and free. If the asset has a fixed-rate pricing schema you need to purchase the corresponding datatoken using $OCEAN. On the other hand for free pricing schema you can obtain a free datatoken from the dispenser service provided by Ocean Protocol.

Additionally, a datatoken associated with an asset can be of 2 types: Template 1 (regular template) and Template 2 (enterprise template). The type of template determines the sequence of method calls required before placing an order.

You can read more about this here.

Buy a fixed-rate asset (Template 1)

Buying an asset with fixed-rate pricing schema and datatoken of type 1 involves 2 steps: buying 1 datatoken with OCEAN tokens and sending it to the publisher to place the order for that asset.

Note: Make sure you have enough $OCEAN in your wallet in order to purchase the asset. You can find here all info about the supported networks

  1. Add a function buyDataToken:

This buys 1 datatoken with $OCEAN from FixedRateExchange.

const buyDataToken = useCallback(async () => {
  const signer = web3Provider.getSigner();
  const address = await web3Provider.getSigner().getAddress();
}, [web3Provider, dataToken?.address]);

Calculate how much $OCEAN we need in order to buy 1 datatoken:

const dataTokenAmount = '1';

const fixedRateExchange = new FixedRateExchange(oceanConfig.fixedRateExchangeAddress!, signer);
const exchangeId = await fixedRateExchange.generateExchangeId(oceanConfig.oceanTokenAddress!, dataToken?.address!);

const priceInfo = await fixedRateExchange.calcBaseInGivenDatatokensOut(exchangeId, dataTokenAmount);
const oceanAmount = priceInfo.baseTokenAmount;

Approve the FixedRateExchange contract to take $OCEAN from your wallet:

const approveResult = await approve(
    signer,
    oceanConfig,
    address,
    oceanConfig.oceanTokenAddress!,
    oceanConfig.fixedRateExchangeAddress!,
    oceanAmount,
);
if (!approveResult) {
  throw new Error('Approve contract failed');
}

/**
 * approveResult can be:
 * - transaction response, in which case we need to wait for the transaction to be executed
 * - a number, which means no transaction was published because there is already an approval for the amount requested
 */
if (typeof approveResult !== 'number') {
  await approveResult.wait(1);
}

Buy 1 datatoken with $OCEAN:

const buyTx = await fixedRateExchange.buyDatatokens(exchangeId, dataTokenAmount, oceanAmount);
if (!buyTx) {
  throw new Error('Buy data token failed');
}

await buyTx.wait(1);
  1. Add a function createOrder:

This creates the order to send the datatoken to the published and buy the asset.

const createOrder = useCallback(async () => {
  const signer = web3Provider.getSigner();
  const address = await web3Provider.getSigner().getAddress();
}, [web3Provider, dataToken?.address, dataTokenService]);

Initialize the provider and add the provider fees:

const initializeData = await ProviderInstance.initialize(
  asset?.id!,
  dataTokenService?.id!,
  0,
  address,
  dataTokenService?.serviceEndpoint!,
);

const providerFees: ProviderFees = {
  providerFeeAddress: initializeData.providerFee.providerFeeAddress,
  providerFeeToken: initializeData.providerFee.providerFeeToken,
  providerFeeAmount: initializeData.providerFee.providerFeeAmount,
  v: initializeData.providerFee.v,
  r: initializeData.providerFee.r,
  s: initializeData.providerFee.s,
  providerData: initializeData.providerFee.providerData,
  validUntil: initializeData.providerFee.validUntil,
};

Create the order:

const dataTokenInstance = new Datatoken(signer);

const tx = await dataTokenInstance.startOrder(dataToken?.address!, address, 0, providerFees);
if (!tx) {
  throw new Error('Create order failed');
}

await tx.wait(1);
  1. Call both functions in onPressBuy:
const onPressBuy = useCallback(async () => {
  setLoading(true);

  try {
    await buyDataToken();
    await createOrder();
    Alert.alert('Success', 'Service bought with success!');
  } catch (error) {
    console.error(error);
  } finally {
    setLoading(false);
  }
}, [buyDataToken, createOrder]);

Buy a fixed-rate asset (Template 2)

Buying an asset with fixed-rate pricing schema and datatoken of type 2 is similar with buying the same asset with data token of type 1, but the process is simplified. The 2 steps involved (buying 1 datatoken and sending it to the publisher) are done in the same transaction.

Note: Make sure you have enough $OCEAN in your wallet in order to purchase the asset. You can find here all info about the supported networks

  1. Add a function getOceanAmount:

This calculates how much $OCEAN we need in order to buy 1 datatoken:

const getOceanAmount = useCallback(async () => {
  const signer = web3Provider.getSigner();
  const dataTokenAmount = '1';

  const fixedRateExchange = new FixedRateExchange(oceanConfig.fixedRateExchangeAddress!, signer);
  const exchangeId = await fixedRateExchange.generateExchangeId(oceanConfig.oceanTokenAddress!, dataToken?.address!);

  const priceInfo = await fixedRateExchange.calcBaseInGivenDatatokensOut(exchangeId, dataTokenAmount);

  return priceInfo.baseTokenAmount;
}, [web3Provider, dataToken?.address]);
  1. Add a function approveContract:

This approves the datatoken contract to take $OCEAN from your wallet:

const approveContract = useCallback(
  async (oceanAmount: string) => {
    const signer = web3Provider.getSigner();
    const address = await web3Provider.getSigner().getAddress();

    const approveResult = await approve(
      signer,
      oceanConfig,
      address,
      oceanConfig.oceanTokenAddress!,
      dataToken?.address!,
      oceanAmount,
    );
    if (!approveResult) {
      throw new Error('Approve contract failed');
    }

    /**
     * approveResult can be:
     * - transaction response, in which case we need to wait for the transaction to be executed
     * - a number, which means no transaction was published because there is already an approval for the amount requested
     */
    if (typeof approveResult !== 'number') {
      await approveResult.wait(1);
    }
  },
  [web3Provider, dataToken?.address],
);
  1. Add a function createOrder:

This creates the order to buy 1 datatoken, send it to the publisher and buy the asset.

const createOrder = useCallback(
  async (oceanAmount: string) => {
    const signer = web3Provider.getSigner();
    const address = await web3Provider.getSigner().getAddress();
  },
  [web3Provider, dataToken?.address!, dataTokenService],
);

Initialize the provider and add the provider fees:

const initializeData = await ProviderInstance.initialize(
  asset?.id!,
  dataTokenService?.id!,
  0,
  address,
  dataTokenService?.serviceEndpoint!,
);
const providerFees: ProviderFees = {
  providerFeeAddress: initializeData.providerFee.providerFeeAddress,
  providerFeeToken: initializeData.providerFee.providerFeeToken,
  providerFeeAmount: initializeData.providerFee.providerFeeAmount,
  v: initializeData.providerFee.v,
  r: initializeData.providerFee.r,
  s: initializeData.providerFee.s,
  providerData: initializeData.providerFee.providerData,
  validUntil: initializeData.providerFee.validUntil,
};

Add the order params:

const orderParams: OrderParams = {
  consumer: address,
  serviceIndex: 0,
  _providerFee: providerFees,
  _consumeMarketFee: {
    consumeMarketFeeAddress: ethers.constants.AddressZero,
    consumeMarketFeeToken: ethers.constants.AddressZero,
    consumeMarketFeeAmount: '0',
  },
};

Add the order params for the FixedRateExchange:

const fixedRateExchange = new FixedRateExchange(oceanConfig.fixedRateExchangeAddress!, signer);
const exchangeId = await fixedRateExchange.generateExchangeId(
  oceanConfig.oceanTokenAddress!,
  dataToken?.address!,
);

const freParams: FreOrderParams = {
  exchangeContract: oceanConfig.fixedRateExchangeAddress!,
  exchangeId,
  maxBaseTokenAmount: oceanAmount || '1',
  baseTokenAddress: oceanConfig.oceanTokenAddress!,
  baseTokenDecimals: 18,
  swapMarketFee: '0',
  marketFeeAddress: ethers.constants.AddressZero,
};

Create the order:

const dataTokenInstance = new Datatoken(signer);

const tx = await dataTokenInstance.buyFromFreAndOrder(dataToken?.address!, orderParams, freParams);
if (!tx) {
  throw new Error('Buy from fixed rate exchange & create order failed');
}

await tx.wait(1);
  1. Call all functions in onPressBuy:
const onPressBuy = useCallback(async () => {
  setLoading(true);

  try {
    const oceanAmount = await getOceanAmount();
    await approveContract(oceanAmount);
    await createOrder(oceanAmount);
    Alert.alert('Success', 'Service bought with success!');
  } catch (error) {
    console.error(error);
  } finally {
    setLoading(false);
  }
}, [getOceanAmount, approveContract, createOrder]);

Buy a free asset (Template 1)

Buying an asset with free pricing schema and datatoken of type 1 involves 2 steps: obtain 1 datatoken from the dispenser and sending it to the publisher to place the order for that asset.

We don't need $OCEAN to buy this type of asset, but we still need tokens to pay the gas fees for each transaction.

  1. Add a function obtainDataToken:

This obtains 1 free datatoken from the dispenser:

const obtainDataToken = useCallback(async () => {
  const signer = web3Provider.getSigner();
  const address = await web3Provider.getSigner().getAddress();

  const dataTokenAmount = '1';
  const dispenser = new Dispenser(oceanConfig.dispenserAddress!, signer);

  const dispenseTx = await dispenser.dispense(dataToken?.address!, dataTokenAmount, address);
  if (!dispenseTx) {
    throw new Error('Dispense data token failed');
  }

  await dispenseTx.wait(1);
}, [web3Provider, dataToken?.address]);
  1. Add a function createOrder:

This creates the order to send the datatoken to the published and buy the asset.

const createOrder = useCallback(async () => {
  const signer = web3Provider.getSigner();
  const address = await web3Provider.getSigner().getAddress();
}, [web3Provider, dataToken?.address, dataTokenService]);

Initialize the provider and add the provider fees:

const initializeData = await ProviderInstance.initialize(
  asset?.id!,
  dataTokenService?.id!,
  0,
  address,
  dataTokenService?.serviceEndpoint!,
);

const providerFees: ProviderFees = {
  providerFeeAddress: initializeData.providerFee.providerFeeAddress,
  providerFeeToken: initializeData.providerFee.providerFeeToken,
  providerFeeAmount: initializeData.providerFee.providerFeeAmount,
  v: initializeData.providerFee.v,
  r: initializeData.providerFee.r,
  s: initializeData.providerFee.s,
  providerData: initializeData.providerFee.providerData,
  validUntil: initializeData.providerFee.validUntil,
};

Create the order:

const dataTokenInstance = new Datatoken(signer);

const tx = await dataTokenInstance.startOrder(dataToken?.address!, address, 0, providerFees);
if (!tx) {
  throw new Error('Create order failed');
}

await tx.wait(1);
  1. Call both functions in onPressBuy:
const onPressBuy = useCallback(async () => {
  setLoading(true);

  try {
    await obtainDataToken();
    await createOrder();
    Alert.alert('Success', 'Service bought with success!');
  } catch (error) {
    console.error(error);
  } finally {
    setLoading(false);
  }
}, [obtainDataToken, createOrder]);;

Buy a free asset (Template 2)

Buying an asset with free pricing schema and datatoken of type 2 is similar with buying a free asset with data token of type 1, but the process is simplified. The 2 steps involved (obtaining 1 datatoken and sending it to the publisher) are done in the same transaction.

We don't need $OCEAN to buy this type of asset, but we still need tokens to pay the gas fees for the transaction.

  1. Add a function createOrder:

This creates the order to obtain 1 datatoken, send it to the publisher and buy the asset.

const createOrder = useCallback(
  async (oceanAmount: string) => {
    const signer = web3Provider.getSigner();
    const address = await web3Provider.getSigner().getAddress();
  },
  [web3Provider, dataToken?.address!, dataTokenService],
);

Initialize the provider and add the provider fees:

const initializeData = await ProviderInstance.initialize(
  asset?.id!,
  dataTokenService?.id!,
  0,
  address,
  dataTokenService?.serviceEndpoint!,
);
const providerFees: ProviderFees = {
  providerFeeAddress: initializeData.providerFee.providerFeeAddress,
  providerFeeToken: initializeData.providerFee.providerFeeToken,
  providerFeeAmount: initializeData.providerFee.providerFeeAmount,
  v: initializeData.providerFee.v,
  r: initializeData.providerFee.r,
  s: initializeData.providerFee.s,
  providerData: initializeData.providerFee.providerData,
  validUntil: initializeData.providerFee.validUntil,
};

Add the order params:

const orderParams: OrderParams = {
  consumer: address,
  serviceIndex: 0,
  _providerFee: providerFees,
  _consumeMarketFee: {
    consumeMarketFeeAddress: ethers.constants.AddressZero,
    consumeMarketFeeToken: ethers.constants.AddressZero,
    consumeMarketFeeAmount: '0',
  },
};

Create the order:

const dataTokenInstance = new Datatoken(signer);

const tx = await dataTokenInstance.buyFromDispenserAndOrder(
  dataToken?.address!,
  orderParams,
  oceanConfig.dispenserAddress!,
);
if (!tx) {
  throw new Error('Buy from dispenser & create order failed');
}

await tx.wait(1);
  1. Call the function in onPressBuy:
const onPressBuy = useCallback(async () => {
  setLoading(true);

  try {
    await createOrder();
    Alert.alert('Success', 'Service bought with success!');
  } catch (error) {
    console.error(error);
  } finally {
    setLoading(false);
  }
}, [createOrder]);

Test the app

  1. Run the app locally
$ npm start
  1. Install the Expo app on your phone and scan the QR code

Note: You need to be on the same network in order to connect to the app. If you cannot connect due to some network conditions/firewall rules you can run the app with tunneling:

$ npm start -- --tunnel

About

No description, website, or topics provided.

Resources

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published