Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Kado OTC provider #5373

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- added: New Kado OTC provider integration.

## 4.17.2

- fixed: (Zcash/Pirate) Fixed android build issues
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
"ethers": "^5.7.2",
"expo": "^48.0.0",
"jsrsasign": "^11.1.0",
"p-debounce": "^4.0.0",
"paraswap": "^5.2.0",
"posthog-js": "^1.88.1",
"posthog-react-native": "^2.8.1",
Expand Down
17 changes: 17 additions & 0 deletions src/components/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { useAsyncEffect } from '../hooks/useAsyncEffect'
import { useMount } from '../hooks/useMount'
import { lstrings } from '../locales/strings'
import { AddressFormScene } from '../plugins/gui/scenes/AddressFormScene'
import { ConfirmationScene } from '../plugins/gui/scenes/ConfirmationScene'
import { EmailFormScene } from '../plugins/gui/scenes/EmailFormScene'
import { FiatPluginEnterAmountScene as FiatPluginEnterAmountSceneComponent } from '../plugins/gui/scenes/FiatPluginEnterAmountScene'
import { FiatPluginWebViewComponent } from '../plugins/gui/scenes/FiatPluginWebView'
import { InfoDisplayScene } from '../plugins/gui/scenes/InfoDisplayScene'
Expand Down Expand Up @@ -296,6 +298,21 @@ const EdgeBuyTabScreen = () => {
headerRight: () => null
}}
/>
<BuyStack.Screen
name="guiPluginConfirmation"
component={ConfirmationScene}
options={{
headerLeft: () => null,
headerRight: () => null
}}
/>
<BuyStack.Screen
name="guiPluginEmailForm"
component={EmailFormScene}
options={{
headerRight: () => null
}}
/>
<BuyStack.Screen
name="guiPluginEnterAmount"
component={FiatPluginEnterAmountScene}
Expand Down
7 changes: 7 additions & 0 deletions src/components/layout/SceneContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { View } from 'react-native'

import { styled } from '../hoc/styled'

export const SceneContainer = styled(View)(theme => ({
padding: theme.rem(0.5)
}))
Comment on lines +1 to +7
Copy link
Contributor

@swansontec swansontec Nov 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

I spot-checked some scenes, and I did notice a lot of <View style={styles.container}> components between the SceneWrapper and the UI elements, so it seems like the need is real. Since this is a SceneContainer specifically, and not a Space, we can tie it in with the SceneWrapper component, such as by understanding the undoInsetStyles or whatever (but this simple version is fine for today).

I think you need to either have a sidebar with Jon, or bring this up at the Monday meeting, so we're all on board with putting this between the SceneWrapper and the contents. We need to get broad adoption across the codebase, so this can start making a positive impact.

7 changes: 7 additions & 0 deletions src/envConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ export const asEnvConfig = asObject({
apiKey: asString
})
),
kadoOtc: asOptional(
asObject({
apiKey: asString,
apiUserEmail: asString
})
),
moonpay: asOptional(asString),
mtpelerin: asOptional(asString),
paybis: asOptional(
Expand Down Expand Up @@ -102,6 +108,7 @@ export const asEnvConfig = asObject({
banxa: undefined,
Bitrefill: undefined,
kado: undefined,
kadoOtc: undefined,
moonpay: undefined,
mtpelerin: undefined,
paybis: undefined,
Expand Down
6 changes: 6 additions & 0 deletions src/locales/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,7 @@ const strings = {
form_field_title_address_line_2: 'Address Line 2 (optional)',
form_field_title_address_state_province_region: 'Province',
form_field_title_address_zip_postal_code: 'Postal Code/Zip',
form_field_title_email_address: 'Email Address',
form_field_title_iban: 'IBAN',
form_field_title_swift_bic: 'SWIFT/BIC',

Expand All @@ -1548,6 +1549,11 @@ const strings = {
sepa_transfer_prompt_s:
'Your order %1$s has been submitted!\n\nPlease save the order details below for your records and instruct your bank to make the payment with the information in the Payment Details section.',

otc_enter_email_to_buy: 'Please enter your email to be contacted by one of our exchange partners to coordinate an OTC (Over the Counter) purchase.',
otc_enter_email_to_sell: 'Please enter your email to be contacted by one of our exchange partners to coordinate an OTC (Over the Counter) sale.',
otc_confirmation_title: 'Request Sent',
otc_confirmation_message: 'Thank you! You will be contacted in the next 24 hours to complete your request.',

// #endregion GuiPlugins

// #region Light Account
Expand Down
5 changes: 5 additions & 0 deletions src/locales/strings/enUS.json
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,7 @@
"form_field_title_address_line_2": "Address Line 2 (optional)",
"form_field_title_address_state_province_region": "Province",
"form_field_title_address_zip_postal_code": "Postal Code/Zip",
"form_field_title_email_address": "Email Address",
"form_field_title_iban": "IBAN",
"form_field_title_swift_bic": "SWIFT/BIC",
"bank_info_title": "Bank Info",
Expand All @@ -1364,6 +1365,10 @@
"bank_transfer_reference": "Reference",
"sepa_form_title": "Enter Bank Info",
"sepa_transfer_prompt_s": "Your order %1$s has been submitted!\n\nPlease save the order details below for your records and instruct your bank to make the payment with the information in the Payment Details section.",
"otc_enter_email_to_buy": "Please enter your email to be contacted by one of our exchange partners to coordinate an OTC (Over the Counter) purchase.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These string changes belong in the next commit, not this one.

"otc_enter_email_to_sell": "Please enter your email to be contacted by one of our exchange partners to coordinate an OTC (Over the Counter) sale.",
"otc_confirmation_title": "Request Sent",
"otc_confirmation_message": "Thank you! You will be contacted in the next 24 hours to complete your request.",
"backup_account": "Back Up Account",
"backup_delete_confirm_message": "Are you sure you want to delete this account without backing up first? You will NOT be able to recover wallets and transactions for this account!",
"backup_info_message": "Create a username and password to create a full account and secure your funds. No personal information is required",
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/gui/amountQuotePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { StateManager } from './hooks/useStateManager'
import { BestError, getBestError, getRateFromQuote } from './pluginUtils'
import { banxaProvider } from './providers/banxaProvider'
import { bityProvider } from './providers/bityProvider'
import { kadoOtcProvider } from './providers/kadoOtcProvider'
import { kadoProvider } from './providers/kadoProvider'
import { moonpayProvider } from './providers/moonpayProvider'
import { mtpelerinProvider } from './providers/mtpelerinProvider'
Expand Down Expand Up @@ -49,7 +50,7 @@ type InternalFiatPluginEnterAmountParams = FiatPluginEnterAmountParams & {
convertValueInternal: (sourceFieldNum: number, value: string, stateManager: StateManager<EnterAmountState>) => Promise<ConvertValueInternalResult>
}

const providerFactories = [banxaProvider, bityProvider, kadoProvider, moonpayProvider, mtpelerinProvider, paybisProvider, simplexProvider]
const providerFactories = [banxaProvider, bityProvider, kadoProvider, kadoOtcProvider, moonpayProvider, mtpelerinProvider, paybisProvider, simplexProvider]

const DEFAULT_FIAT_AMOUNT = '500'
const DEFAULT_FIAT_AMOUNT_LIGHT_ACCOUNT = '50'
Expand Down Expand Up @@ -547,7 +548,7 @@ export const amountQuoteFiatPlugin: FiatPluginFactory = async (params: FiatPlugi
// showing the quotes.
// TODO: conflict: also defines whether or not to accept a quote from the
// provider
export const createPriorityArray = (providerPriority: ProviderPriorityMap): PriorityArray => {
export const createPriorityArray = (providerPriority: ProviderPriorityMap | undefined): PriorityArray => {
const priorityArray: PriorityArray = []
if (providerPriority != null) {
const temp: Array<{ pluginId: string; priority: number }> = []
Expand Down
26 changes: 26 additions & 0 deletions src/plugins/gui/fiatPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ export const executePlugin = async (params: {
buttonModal: async params => {
return await Airship.show(bridge => <ButtonsModal bridge={bridge} {...params} />)
},
confirmation: async params => {
return await new Promise(resolve => {
maybeNavigateToCorrectTabScene()
navigation.navigate('guiPluginConfirmation', {
title: params.title,
message: params.message,
onClose: async () => {
resolve()
}
})
})
},
showToastSpinner,
openWebView: async (params): Promise<void> => {
maybeNavigateToCorrectTabScene()
Expand Down Expand Up @@ -170,6 +182,20 @@ export const executePlugin = async (params: {
maybeNavigateToCorrectTabScene()
navigation.navigate('guiPluginEnterAmount', params)
},
async emailForm(params) {
return await new Promise((resolve, reject) => {
maybeNavigateToCorrectTabScene()
navigation.navigate('guiPluginEmailForm', {
message: params.message,
onSubmit: async (email: string) => {
resolve(email)
},
onClose: async () => {
resolve(undefined)
}
})
})
},
addressForm: async params => {
const { countryCode, headerTitle, headerIconUri, onSubmit } = params
return await new Promise((resolve, reject) => {
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/gui/fiatPluginTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,15 @@ export type FiatPluginPermissions = Permission[]
export interface FiatPluginUi {
addressWarnings: (parsedUri: any, currencyCode: string) => Promise<boolean>
buttonModal: <Buttons extends { [key: string]: ButtonInfo }>(params: Omit<ButtonModalProps<Buttons>, 'bridge'>) => Promise<keyof Buttons | undefined>
confirmation: (params: { title: string; message: string }) => Promise<void>
showToastSpinner: <T>(message: string, promise: Promise<T>) => Promise<T>
openWebView: (params: FiatPluginOpenWebViewParams) => Promise<void>
openExternalWebView: (params: FiatPluginOpenExternalWebViewParams) => Promise<void>
walletPicker: (params: { headerTitle: string; allowedAssets?: EdgeAsset[]; showCreateWallet?: boolean }) => Promise<FiatPluginWalletPickerResult | undefined>
showError: (error: unknown) => Promise<void>
listModal: (params: FiatPluginListModalParams) => Promise<string | undefined>
enterAmount: (params: AppParamList['guiPluginEnterAmount']) => void
emailForm: (params: { message?: string }) => Promise<string | undefined>
addressForm: (params: FiatPluginAddressFormParams) => Promise<HomeAddress | undefined>
requestPermission: (permissions: FiatPluginPermissions, displayName: string, mandatory: boolean) => Promise<boolean>
rewardsCardDashboard: (params: RewardsCardDashboardParams) => Promise<void>
Expand Down
20 changes: 20 additions & 0 deletions src/plugins/gui/hooks/useFormFieldState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useState } from 'react'

import { useHandler } from '../../../hooks/useHandler'

interface FieldState {
onChangeText: (text: string) => void
value: string
}
export const useFormFieldState = (defaultValue: string = ''): FieldState => {
const [value, setValue] = useState<string>(defaultValue)

const handleChange = useHandler(text => {
setValue(text)
})
Comment on lines +12 to +14
Copy link
Contributor

@swansontec swansontec Nov 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this useFormFieldState abstraction. It is creating cognitive overhead and without providing any real value.

There is no need to wrap setValue with a no-op useHandler. You can pass setValue straight to the onChangeText prop. Once you do that, it's just as simple to use useState directly, and not even bother with this useFormFieldState thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we're suppose to use useHandler for all event handler function by convention? The function reference is stable with useHandler so this abstraction leverages that stability.

I can remove this abstraction completely though.


return {
onChangeText: handleChange,
value
}
}
Loading