Docs
Search…
⌃K
Links

React Native

The Ratio React Native SDK allows partners to embed and connect to Ratio’s application from their React Native application.
The React Native SDK provides a RatioComponent component that requires a few parameters (listed below). This component allows for a few customizations so that it can be placed anywhere within your app.
The RatioComponent takes a child view that is wrapped using a <TouchableOpacity/> component. This means that whatever view is passed into the RatioComponent will be clickable. Our suggestion is to use the view as a custom "button" to allow users access Ratio services.

Requirements

To use the SDK you must first acquire a clientId and clientSecret from the Ratio team. Learn how to request API keys here.
Ratio will provide an API that you will be required to wrap in your own back end (see example below)

Installation

Library version
npm install @ratio.me/ratio-react-native-library
or
yarn add @ratio.me/ratio-react-native-library

Peer Dependencies

Our library has a peer dependancy on the following - react-native-webview: "11.x" - react-native-svg: "12.x || 13.x"
This means you must add react-native-webview and react-native-svg as dependancies by running the following
npm install react-native-webview --save
npm install react-native-svg --save
or
yarn add react-native-webview --save
yarn add react-native-svg --save
NOTE: it is important to make sure you use the --save flag, this will make sure the native code is autolinked. Read more about React Native Auto linking here
Read more about react-native-webview here
Read more about react-native-svg here

Usage

1
import * as React from 'react';
2
import { useEffect, useState } from 'react';
3
4
import {
5
ActivityIndicator,
6
Alert,
7
Platform,
8
SafeAreaView,
9
StatusBar,
10
StyleSheet,
11
Text,
12
View,
13
} from 'react-native';
14
import Web3 from 'web3';
15
import {
16
RatioComponent,
17
RatioOrderStatus,
18
} from '@ratio.me/ratio-react-native-library';
19
import * as LocalAuthentication from 'expo-local-authentication';
20
import type { LocalAuthenticationResult } from 'expo-local-authentication';
21
22
const styles = StyleSheet.create({
23
AndroidSafeArea: {
24
flex: 1,
25
backgroundColor: 'white',
26
paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0,
27
},
28
buttonWrapper: {
29
display: 'flex',
30
alignItems: 'center',
31
},
32
buyCryptoButton: {
33
backgroundColor: 'black',
34
width: 185,
35
height: 50,
36
display: 'flex',
37
alignItems: 'center',
38
justifyContent: 'center',
39
borderRadius: 14,
40
},
41
buyText: {
42
color: 'white',
43
},
44
});
45
46
const provider ='WEB3_PROVIDER_URL';
47
const privateKey =
48
'WALLET_PRIVATE_KEY';
49
const walletSigningAddress = 'SIGNING_ADDRESS';
50
const walletNetwork = 'WALLET_NETWORK';
51
const walletDepositAddress = 'DEPOSIT_ADDRESS';
52
53
const web3 = new Web3(new Web3.providers.HttpProvider(provider));
54
55
export default function App() {
56
const [loading, setLoading] = React.useState(false);
57
const [isBiometricSupported, setIsBiometricSupported] = useState(false);
58
const [ratioUser, setRatioUser] = useState(null)
59
60
useEffect(() => {
61
(async () => {
62
const compatible = await LocalAuthentication.hasHardwareAsync();
63
setIsBiometricSupported(compatible);
64
})();
65
});
66
67
const fetchSessionToken = async () => {
68
try {
69
let sessionTokenResponse = await fetch(
70
'https://your.api.com/clients/session',
71
{
72
method: 'POST',
73
body: JSON.stringify({
74
signingAddress: walletAddress,
75
depositAddress: walletAddress,
76
signingNetwork: walletNetwork,
77
}),
78
}
79
);
80
81
let data = await sessionTokenResponse.json();
82
return data.id;
83
} catch (e) {
84
console.error(e);
85
}
86
return null;
87
};
88
89
const fallBackToDefaultAuth = () => {};
90
const handleBiometricAuth = async () => {
91
const savedBiometrics = await LocalAuthentication.isEnrolledAsync();
92
if (!savedBiometrics) {
93
Alert.alert(
94
'Biometric record not found',
95
'Please verify your identity with your password',
96
[{ text: 'OK' }],
97
{ onDismiss: () => fallBackToDefaultAuth() }
98
);
99
return {
100
success: false,
101
error: 'no saved biometrics',
102
} as LocalAuthenticationResult;
103
}
104
const biometricAuth = await LocalAuthentication.authenticateAsync({
105
promptMessage: 'Login with Biometrics',
106
disableDeviceFallback: true,
107
cancelLabel: 'Cancel',
108
});
109
110
return biometricAuth;
111
};
112
113
return (
114
<SafeAreaView style={styles.AndroidSafeArea}>
115
{loading ? <ActivityIndicator /> : null}
116
<Text>
117
{isBiometricSupported
118
? 'Your device is compatible with Biometrics'
119
: 'Face or Fingerprint scanner is available on this device'}
120
</Text>
121
<View style={styles.buttonWrapper}>
122
<RatioComponent
123
fetchSessionToken={async () => {
124
return await fetchSessionToken();
125
}}
126
signingCallback={async (challenge: string) => {
127
let result = await handleBiometricAuth();
128
if (result.success) {
129
let sign = web3.eth.accounts.sign(challenge, privateKey);
130
131
return Promise.resolve({
132
signature: sign.signature,
133
});
134
} else {
135
return Promise.reject('failed biometrics');
136
}
137
}}
138
onPress={() => {
139
setLoading(true);
140
}}
141
onOpen={() => setLoading(false)}
142
onTransactionComplete={(orderStatus: RatioOrderStatus) => {
143
Alert.alert(orderStatus.status);
144
}}
145
onHelp={() => {
146
Alert.alert('Help button pressed');
147
}}
148
onAccountRecovery={() => {
149
Alert.alert('Account recovery button pressed');
150
}}
151
onError={(errorMessage: string) => {
152
setLoading(false);
153
Alert.alert(errorMessage);
154
}}
155
onClose={() => {}}
156
onLogin={(user: RatioUser)=> { setRatioUser(user) }}
157
>
158
{/* view used as visible 'button' to press */}
159
<View style={styles.buyCryptoButton}>
160
<Text style={styles.buyText}>Buy Crypto</Text>
161
</View>
162
</RatioComponent>
163
</View>
164
</SafeAreaView>
165
);
166
}

Reference

Props

fetchSessionToken

A function that is used to fetch session token that is generated from the API that is used to wrap the ratio /v1/clients/session (documentation here)
This is an async function.
The Ratio API uses client authentication which requires a client_id and client_secret. It is highly recommended to implement this call in a secure API backend. This will prevent the need of shipping the clientSecret with the client application.
const fetchSessionToken = async () => {
try {
let sessionTokenResponse = await fetch(
'https://your.api.com/clients/session',
{
method: 'POST',
body: JSON.stringify({
signingAddress: walletAddress,
depositAddress: walletAddress,
signingNetwork: walletNetwork,
}),
}
);
let data = await sessionTokenResponse.json();
return data.id;
} catch (e) {
console.error(e);
}
return null;
};
<RatioComponent
fetchSessionToken={async () => {
return await fetchSessionToken();
}}/>
TYPE
REQUIRED
function
Yes

signingCallback

Function that accepts a string which contains the challenge that is returned from the Ratio /v1/auth/cryptoWallet:start call (documentation here)
This is an async function that should return a promise. This will allow such asynchronous activities such as a biometrics check to happen during signing.
The return value from this function is of type RatioKitSigningResult. See the Models section below.
Example using Web3.js library
<RatioComponent
signingCallback={async (challenge: string) => {
let sign = web3.eth.accounts.sign(challenge, privateKey);
return Promise.resolve({
signature: sign.signature,
});
}}/>
TYPE
REQUIRED
function
Yes

onPress

A function that is called when the child view is pressed. This function is called before the SDK starts the authentication flow. Suggested uses include setting an ActivityIndicator or loading spinner to be visible.
Example
const [loading, setLoading] = useState(false)
<RatioComponent onPress={()=> {
setLoading(true)
}}/>
TYPE
REQUIRED
function
No

onOpen

A function that is called once the SDK has completed its authentication flow and before the modal is displayed. Suggested uses include setting an ActivityIndicator or loading spinner to be hidden
Example
const [loading, setLoading] = useState(false)
<RatioComponent onOpen={()=> {
setLoading(false)
}}/>
TYPE
REQUIRED
function
No

onTransactionComplete

A function that is called whenever a transaction is completed even if there was a failure. As part of the data in the RatioOrderStatus object, you will receive the userId for the user that completed the transaction and the ActivityItem detailing the transaction. The userId and activityItem will not be returned if there was an error in processing the order.
Example
<RatioComponent onTransactionComplete={(orderStatus: RatioOrderStatus)=> {
Alert.alert(orderStatus.status)
}}/>
TYPE
REQUIRED
function
No

onHelp

A function that is called whenever a help button is pressed from within the RatioComponent Modal WebView. This callback allows for custom handling for when the user needs help.
If not provided, the default behaviour is to open the default email client and draft an email to [email protected]
Example
<RatioComponent onHelp={()=> {
Alert.alert(orderStatus.status)
}}/>
TYPE
REQUIRED
function
No

onAccountRecovery

A function that is called whenever the account recovery button is pressed from within the RatioComponent Modal WebView. This callback allows for custom handling for when the user needs help recovering their account.
If not provided, the default behaviour is to open the default email client and draft an email to [email protected]
Example
<RatioComponent onAccountRecovery={()=> {
Alert.alert(orderStatus.status)
}}/>
TYPE
REQUIRED
function
No

onError

A function that is called whenever an error occurs within the Ratio component's authorization flow.
If not provided, the default behaviour is to show an Alert dialog with an error message
Example
<RatioComponent onError={(errorMessage: string)=> {
Alert.alert(errorMessage)
}}/>
TYPE
REQUIRED
function
No

onClose

A function that is called after the Ratio Modal WebView. There is no default behaviour if not provided. As of writing this documentation this function will be called when the user presses "Return to wallet" in the application. It will then close the React Native modal and then call "onClose"
TYPE
REQUIRED
function
No

onLogin

A function that is called when the a user is fully authenticated. It is also called when the user's account has been created. The callback returns a RatioUser object that is described below.
Example
<RatioComponent onLogin={(user: RatioUser)=> {
Alert.alert(user.firstName)
}}/>

Models

RatioKitSigningResult

export interface RatioKitSigningResult {
signature: string;
}

RatioOrderStatus

export interface RatioOrderStatus {
data: {userId: string, activity: ActivityItem};
status: 'success' | 'failure';
error: OrderError;
}

OrderError

export interface OrderError {
errorId: string;
message: string;
statusCode: number;
}

ActivityItem

export interface ActivityItem {
id: string;
createTime: string;
updateTime: string;
fiat: ActivityItemFiat;
crypto: ActivityItemCrypto;
metadata: any;
}

ActivityItemFiat

export interface ActivityItemFiat {
status: ActivityItemStatus;
currency: FiatCurrency;
amount: string;
direction: Direction;
fundingMethod: FundingMethod;
bankAccount: BankAccount;
}

ActivityItemStatus

export enum ActivityItemStatus {
PENDING = 'PENDING',
COMPLETED = 'COMPLETED',
FAILED = 'FAILED'
}

FiatCurrency

export enum FiatCurrency {
USD = 'USD'
}

Direction

export enum Direction {
CREDIT = 'CREDIT',
DEBIT = 'DEBIT'
}

FundingMethod

export enum FundingMethod {
ACH_ORIGINATED_STANDARD = 'ACH_ORIGINATED_STANDARD',
ACH_ORIGINATED_INSTANT = 'ACH_ORIGINATED_INSTANT',
ACH_RECEIVED = 'ACH_RECEIVED'
}

BankAccount

export interface BankAccount {
id: string;
createTime: string;
updateTime: string;
name: string;
mask: string;
linkStatus: LinkStatus;
verificationStatus: VerificationStatus;
}

LinkStatus

export enum LinkStatus {
INACTIVE = 'INACTIVE',
ACTIVE = 'ACTIVE',
LOGIN_REQUIRED = 'LOGIN_REQUIRED'
}

VerificationStatus

export enum VerificationStatus {
IN_REVIEW = 'IN_REVIEW',
APPROVED = 'APPROVED',
DECLINED = 'DECLINED'
}

ActivityItemCrypto

export interface ActivityItemCrypto {
status: ActivityItemStatus;
currency: string;
wallet: Wallet;
direction: Direction;
amount: string;
price: string;
fee: string;
transactionHash: string;
}

Wallet

export interface Wallet {
id: string;
address: string;
createTime: string;
name: string;
network: string;
updateTime: string;
}

RatioUser

export interface RatioUser {
id: string;
createTime: string;
updateTime: string;
firstName: string;
middleName: string;
lastName: string;
email: string;
country: string;
phone: string;
preferredMfaMethod: TwoFactorMethod;
nationality: string;
occupation: string;
kyc: Kyc;
connectedBankAccounts: BankAccount[];
}

TwoFactorMethod

export enum TwoFactorMethod {
OTP_SMS = 'OTP_SMS',
TOTP = 'TOTP'
}

BankAccount

export interface BankAccount {
id: string;
createTime: string;
updateTime: string;
name: string;
mask: string;
linkStatus: LinkStatus;
verificationStatus: VerificationStatus;
}

LinkStatus

export enum LinkStatus {
INACTIVE = 'INACTIVE',
ACTIVE = 'ACTIVE',
LOGIN_REQUIRED = 'LOGIN_REQUIRED'
}

VerificationStatus

export enum VerificationStatus {
IN_REVIEW = 'IN_REVIEW',
APPROVED = 'APPROVED',
DECLINED = 'DECLINED'
}

Kyc

export type Kyc = {
createTime: string;
updateTime: string;
addressResult: KycResult;
dobResult: KycResult;
fraudResult: KycResult;
idvResult: KycResult;
};

KycResult

export enum KycResult {
UNKNOWN = 'UNKNOWN',
NOT_STARTED = 'NOT_STARTED',
SUBMITTED = 'SUBMITTED',
IN_REVIEW = 'IN_REVIEW',
APPROVED = 'APPROVED',
DECLINED = 'DECLINED'
}

Appendix

Sequence diagram