Skip to content

Commit

Permalink
Merge pull request #530 from cmgustavo/bug/topup-coinbase-matic-01
Browse files Browse the repository at this point in the history
Fix: create invoice for payments with Coinbase
  • Loading branch information
JohnathanWhite authored Nov 9, 2022
2 parents c888a97 + 0a2f00a commit f928b6a
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 31 deletions.
5 changes: 5 additions & 0 deletions src/api/coinbase/coinbase.constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
CoinbaseEnvironment,
CoinbaseSupportedNetwork,
ConfigApiProps,
CredentialsProps,
} from './coinbase.types';
Expand Down Expand Up @@ -52,3 +53,7 @@ export const COINBASE_INVOICE_URL: string =
export const COINBASE_ENV: CoinbaseEnvironment = __DEV__
? CoinbaseEnvironment.sandbox
: CoinbaseEnvironment.production;
export const COINBASE_HOST_NETWORK = {
ethereum: CoinbaseSupportedNetwork.ethereum,
polygon: CoinbaseSupportedNetwork.polygon,
};
5 changes: 5 additions & 0 deletions src/api/coinbase/coinbase.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,8 @@ export enum CoinbaseEnvironment {
sandbox = 'sandbox',
production = 'production',
}

export enum CoinbaseSupportedNetwork {
ethereum = 'eth',
polygon = 'matic',
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
} from '../../../../../api/coinbase/coinbase.types';
import {coinbasePayInvoice} from '../../../../../store/coinbase';
import {useTranslation} from 'react-i18next';
import {getTransactionCurrencyForPayInvoice} from '../../../../../store/coinbase/coinbase.effects';

export interface DebitCardConfirmParamList {
amount: number;
Expand Down Expand Up @@ -181,10 +182,15 @@ const Confirm = () => {

const onCoinbaseAccountSelect = async (walletRowProps: WalletRowProps) => {
const selectedCoinbaseAccount = walletRowProps.coinbaseAccount!;
const transactionCurrency = dispatch(
getTransactionCurrencyForPayInvoice(
selectedCoinbaseAccount.currency.code,
),
);
try {
const {invoice: newInvoice} = await createTopUpInvoice({
walletId: selectedCoinbaseAccount.id,
transactionCurrency: selectedCoinbaseAccount.currency.code,
transactionCurrency,
});
const rates = await dispatch(startGetRates({}));
const newTxDetails = dispatch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
GiftCardScreens,
GiftCardStackParamList,
} from '../../../../tabs/shop/gift-card/GiftCardStack';
import {getTransactionCurrencyForPayInvoice} from '../../../../../store/coinbase/coinbase.effects';

export interface GiftCardConfirmParamList {
amount: number;
Expand Down Expand Up @@ -207,10 +208,15 @@ const Confirm = () => {

const onCoinbaseAccountSelect = async (walletRowProps: WalletRowProps) => {
const selectedCoinbaseAccount = walletRowProps.coinbaseAccount!;
const transactionCurrency = dispatch(
getTransactionCurrencyForPayInvoice(
selectedCoinbaseAccount.currency.code,
),
);
try {
const {invoice: newInvoice} = await createGiftCardInvoice({
clientId: selectedCoinbaseAccount.id,
transactionCurrency: selectedCoinbaseAccount.currency.code,
transactionCurrency,
});
const rates = await dispatch(startGetRates({}));
const newTxDetails = dispatch(
Expand Down
8 changes: 8 additions & 0 deletions src/store/coinbase/coinbase.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
CoinbaseEnvironment,
CoinbaseErrorsProps,
CoinbaseExchangeRatesProps,
CoinbaseHostNetwork,
CoinbaseTokenProps,
CoinbaseTransactionsProps,
CoinbaseUserProps,
Expand Down Expand Up @@ -205,3 +206,10 @@ export const toggleHideCoinbaseTotalBalance = (
type: CoinbaseActionTypes.TOGGLE_HIDE_TOTAL_BALANCE,
payload: hideTotalBalance,
});

export const selectHostNetwork = (
selectedHostNetwork: CoinbaseHostNetwork,
): CoinbaseActionType => ({
type: CoinbaseActionTypes.SELECTED_HOST_NETWORK,
payload: selectedHostNetwork,
});
17 changes: 16 additions & 1 deletion src/store/coinbase/coinbase.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {SupportedCurrencyOptions} from '../../constants/SupportedCurrencyOptions
import {LogActions} from '../log';
import {setHomeCarouselConfig} from '../app/app.actions';
import {logSegmentEvent} from '../app/app.effects';
import {getCurrencyCodeFromCoinAndChain} from '../../navigation/bitpay-id/utils/bitpay-id-utils';

const isRevokedTokenError = (error: CoinbaseErrorsProps): boolean => {
return error?.errors?.some(err => err.id === 'revoked_token');
Expand Down Expand Up @@ -91,6 +92,16 @@ export const coinbaseParseErrorToString = (
}
};

export const getTransactionCurrencyForPayInvoice =
(currency: string): Effect<string> =>
(dispatch, getState) => {
const {COINBASE} = getState();
return getCurrencyCodeFromCoinAndChain(
currency,
COINBASE.blockchainNetwork,
);
};

export const coinbaseInitialize =
(): Effect<Promise<any>> => async (dispatch, getState) => {
const {COINBASE} = getState();
Expand Down Expand Up @@ -512,12 +523,16 @@ export const coinbasePayInvoice =
return;
}

const transactionCurrency = dispatch(
getTransactionCurrencyForPayInvoice(currency),
);

try {
dispatch(LogActions.info('coinbasePayInvoice: starting...'));
dispatch(payInvoicePending());
await CoinbaseAPI.payInvoice(
invoiceId,
currency,
transactionCurrency,
COINBASE.token[COINBASE_ENV],
code,
);
Expand Down
11 changes: 11 additions & 0 deletions src/store/coinbase/coinbase.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import {
CoinbaseEnvironment,
CoinbaseErrorsProps,
CoinbaseExchangeRatesProps,
CoinbaseSupportedNetwork,
CoinbaseTokenProps,
CoinbaseTransactionsByAccountProps,
CoinbaseUserProps,
} from '../../api/coinbase/coinbase.types';
import {EVM_CHAINS} from '../../constants/currencies';
import {
ApiLoading,
CoinbaseActionType,
Expand Down Expand Up @@ -62,6 +64,7 @@ export interface CoinbaseState {
payInvoiceError: CoinbaseErrorsProps | null;
exchangeRates: CoinbaseExchangeRatesProps | null;
hideTotalBalance: boolean;
blockchainNetwork: EVM_CHAINS;
token: {
[key in CoinbaseEnvironment]: CoinbaseTokenProps | null;
};
Expand Down Expand Up @@ -99,6 +102,8 @@ const initialState: CoinbaseState = {
payInvoiceError: null,
exchangeRates: null,
hideTotalBalance: false,
// Other chain is not supported by Coinbase API
blockchainNetwork: CoinbaseSupportedNetwork.ethereum,
token: {
[CoinbaseEnvironment.production]: null,
[CoinbaseEnvironment.sandbox]: null,
Expand Down Expand Up @@ -418,6 +423,12 @@ export const coinbaseReducer = (
hideTotalBalance: action.payload,
};

case CoinbaseActionTypes.BLOCKCHAIN_NETWORK:
return {
...state,
blockchainNetwork: action.payload,
};

default:
return {...state};
}
Expand Down
10 changes: 9 additions & 1 deletion src/store/coinbase/coinbase.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CoinbaseTransactionsByAccountProps,
CoinbaseEnvironment,
} from '../../api/coinbase/coinbase.types';
import {EVM_CHAINS} from '../../constants/currencies';

export type ApiLoading = boolean;
export type GetAccessTokenStatus = 'success' | 'failed' | null;
Expand Down Expand Up @@ -50,6 +51,7 @@ export enum CoinbaseActionTypes {
PAY_INVOICE_SUCCESS = 'Coinbase/PAY_INVOICE_SUCCESS',
PAY_INVOICE_FAILED = 'Coinbase/PAY_INVOICE_FAILED',
TOGGLE_HIDE_TOTAL_BALANCE = 'Coinbase/TOGGLE_HIDE_TOTAL_BALANCE',
BLOCKCHAIN_NETWORK = 'Coinbase/BLOCKCHAIN_NETWORK',
}

// ------- Exchange Rate -------- //
Expand Down Expand Up @@ -219,6 +221,11 @@ interface ToggleHideCoinbaseTotalBalance {
payload: boolean;
}

interface SetBlockchainNetwork {
type: typeof CoinbaseActionTypes.BLOCKCHAIN_NETWORK;
payload: EVM_CHAINS;
}

export type CoinbaseActionType =
| ExchangeRatesPending
| ExchangeRatesSuccess
Expand Down Expand Up @@ -250,4 +257,5 @@ export type CoinbaseActionType =
| PayInvoiceSuccess
| PayInvoiceFailed
| ClearErrorStatus
| ToggleHideCoinbaseTotalBalance;
| ToggleHideCoinbaseTotalBalance
| SetBlockchainNetwork;
1 change: 1 addition & 0 deletions src/store/coinbase/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export {
payInvoicePending,
payInvoiceSuccess,
payInvoiceFailed,
selectHostNetwork,
} from './coinbase.actions';
export {
isInvalidTokenError,
Expand Down
58 changes: 31 additions & 27 deletions src/store/wallet/effects/send/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,7 @@ import {
} from '../../../../navigation/wallet/components/ErrorMessages';
import {BWCErrorMessage, getErrorName} from '../../../../constants/BWCError';
import {Invoice} from '../../../shop/shop.models';
import {
GetPayProDetails,
HandlePayPro,
PayProOptions,
GetInvoiceCurrency,
} from '../paypro/paypro';
import {GetPayProDetails, HandlePayPro, PayProOptions} from '../paypro/paypro';
import {
checkingBiometricForSending,
dismissBottomNotificationModal,
Expand All @@ -71,7 +66,7 @@ import {
} from '../../../../constants/BiometricError';
import {Platform} from 'react-native';
import {Rates} from '../../../rate/rate.models';
import {getCurrencyCodeFromCoinAndChain} from '../../../../navigation/bitpay-id/utils/bitpay-id-utils';
import {getCoinAndChainFromCurrencyCode} from '../../../../navigation/bitpay-id/utils/bitpay-id-utils';

export const createProposalAndBuildTxDetails =
(
Expand Down Expand Up @@ -349,7 +344,10 @@ export const getInvoiceEffectiveRate =
return (
precision &&
invoice.price /
(invoice.paymentSubtotals[coin.toUpperCase()] / precision.unitToSatoshi)
(invoice.paymentSubtotals[
invoice.buyerProvidedInfo!.selectedTransactionCurrency!
] /
precision.unitToSatoshi)
);
};

Expand Down Expand Up @@ -377,24 +375,29 @@ export const buildTxDetails =
feeLevel?: string;
}): Effect<TxDetails> =>
dispatch => {
const {coin, fee, gasPrice, gasLimit, nonce, destinationTag, chain} =
proposal || {
coin: invoice!.buyerProvidedInfo!.selectedTransactionCurrency!.toLowerCase(),
chain:
invoice!.buyerProvidedInfo!.selectedTransactionCurrency!.toLowerCase(), // TODO POLYGON
fee: 0,
};
const invoiceCurrency = getCurrencyCodeFromCoinAndChain(
GetInvoiceCurrency(coin).toLowerCase(),
chain,
);
let {amount} = proposal || {
amount: invoice!.paymentTotals[invoiceCurrency],
};
const {gasPrice, gasLimit, nonce, destinationTag} = proposal || {};
const invoiceCurrency =
invoice?.buyerProvidedInfo!.selectedTransactionCurrency;
let {amount, coin, chain, fee} = proposal || {};
if (invoiceCurrency) {
amount = invoice.paymentTotals[invoiceCurrency] || 0;
const coinAndChain = getCoinAndChainFromCurrencyCode(
invoiceCurrency.toLowerCase(),
);
coin = coinAndChain.coin;
chain = coinAndChain.chain;
fee = invoice.minerFees[invoiceCurrency].totalFee || 0;
}

if (!coin || !chain) {
throw new Error('Invalid coin or chain');
}

amount = Number(amount); // Support BN (use number instead string only for view)
const effectiveRate =
invoice &&
dispatch(getInvoiceEffectiveRate(invoice, invoiceCurrency, chain));
(invoiceCurrency &&
dispatch(getInvoiceEffectiveRate(invoice, invoiceCurrency, chain))) ||
undefined;
const opts = {
effectiveRate,
defaultAltCurrencyIsoCode,
Expand All @@ -403,12 +406,13 @@ export const buildTxDetails =
chain,
};
const rateStr = getRateStr(opts);
const networkCost = invoice?.minerFees[invoiceCurrency]?.totalFee;
const networkCost =
invoiceCurrency && invoice?.minerFees[invoiceCurrency]?.totalFee;
const isERC20 = IsERCToken(coin, chain);
const effectiveRateForFee = isERC20 ? undefined : effectiveRate; // always use chain rates for fee values

if (context === 'paypro') {
amount = invoice!.paymentTotals[invoiceCurrency];
if (invoiceCurrency && context === 'paypro') {
amount = invoice.paymentTotals[invoiceCurrency];
} else if (context === 'speedupBtcReceive') {
amount = amount - fee;
}
Expand Down

0 comments on commit f928b6a

Please sign in to comment.