Demo React Native app using ocean.js to buy assets
- 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)
- Install dependencies
$ npm install
- Create the
.env
file from the env example file
$ cp .env.example .env
- Add the necessary env variables
- Run the app
$ npm start
- 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
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.
- Prerequisites
- Create a new Expo project with Typescript
- Connect a wallet
- Create a Web3 provider
- Add Ocean.js
- Buy an asset
- Test the app
- node.js (Install from here).
- phone (Android or iOS) with Expo Go installed.
$ npx create-expo-app <your_project_name> -t expo-template-blank-typescript
$ cd <your_project_name>
-
Register on WalletConnect and create a project. We'll need the Project ID later.
-
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
- Create a
.env
file and add the following:
EXPO_PUBLIC_CHAIN_ID=
EXPO_PUBLIC_RPC_URL=
EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID=
- 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';
- 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>
);
}
- 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>
);
-
Register on Magic Link and create a project. We'll need the API key later.
-
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
- Create a
.env
file and add the following:
EXPO_PUBLIC_CHAIN_ID=
EXPO_PUBLIC_RPC_URL=
EXPO_PUBLIC_MAGIC_API_KEY=
- Add a file
magic.ts
and create anMagic 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;
- 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>
);
}
- 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>
);
- 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();
}, []);
- 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>
)}
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]);
- Install library:
$ npm install @oceanprotocol/lib
- Add an env var for the asset DID:
EXPO_PUBLIC_OCEAN_DID=
- 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.
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).
We are going to add a component that loads an asset with a button to purchase it.
- Create a simple file
BuyAsset.tsx
insrc/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>
)}
- 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]);
- 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>
);
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.
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
- 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);
- 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);
- 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]);
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
- 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]);
- 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],
);
- 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);
- 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]);
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.
- 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]);
- 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);
- 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]);;
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.
- 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);
- 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]);
- Run the app locally
$ npm start
- 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