From 2013a8e3c92c09c3c112798a47992ac5e0a8d2a0 Mon Sep 17 00:00:00 2001 From: Andrew Reder Date: Tue, 27 Aug 2024 19:20:18 -0700 Subject: [PATCH 1/8] add hydrate charge utility --- src/pay/constants.ts | 3 +++ src/pay/types.ts | 21 +++++++++++++++ src/pay/utils/getPayErrorCode.ts | 15 +++++++++++ src/pay/utils/hydrateCharge.ts | 45 ++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 src/pay/constants.ts create mode 100644 src/pay/types.ts create mode 100644 src/pay/utils/getPayErrorCode.ts create mode 100644 src/pay/utils/hydrateCharge.ts diff --git a/src/pay/constants.ts b/src/pay/constants.ts new file mode 100644 index 000000000..c36655f15 --- /dev/null +++ b/src/pay/constants.ts @@ -0,0 +1,3 @@ +export const GENERAL_PAY_ERROR_CODE = "PAY_ERROR" +export const PAY_TOO_MANY_REQUESTS_ERROR_CODE = 'TOO_MANY_REQUESTS_ERROR'; +export const PAY_INVALID_PARAMETER_ERROR_CODE = "PAY_INVALID_PARAMETER_ERROR" \ No newline at end of file diff --git a/src/pay/types.ts b/src/pay/types.ts new file mode 100644 index 000000000..5c7a18445 --- /dev/null +++ b/src/pay/types.ts @@ -0,0 +1,21 @@ +export type HydrateChargeAPIResponse = { + id: string; + callData: { + deadline: string; + feeAmount: string; + id: string; + operator: `0x${string}`; + prefix: `0x${string}`; + recipient: `0x${string}`; + recipientAmount: string; + recipientCurrency: `0x${string}`; + refundDestination: `0x${string}`; + signature: `0x${string}`; + }; + metaData: { + chainId: number; + contractAddress: `0x${string}`; + sender: `0x${string}`; + settlementCurrencyAddress: `0x${string}`; + }; +}; diff --git a/src/pay/utils/getPayErrorCode.ts b/src/pay/utils/getPayErrorCode.ts new file mode 100644 index 000000000..deb58309a --- /dev/null +++ b/src/pay/utils/getPayErrorCode.ts @@ -0,0 +1,15 @@ +import { GENERAL_PAY_ERROR_CODE, PAY_INVALID_PARAMETER_ERROR_CODE, PAY_TOO_MANY_REQUESTS_ERROR_CODE } from "../constants"; + +export function getPayErrorCode( + errorCode?: number, + ) { + if (errorCode === -32001) { + return PAY_TOO_MANY_REQUESTS_ERROR_CODE; + } + + if (errorCode === -32602) { + return PAY_INVALID_PARAMETER_ERROR_CODE; + } + + return GENERAL_PAY_ERROR_CODE; + } \ No newline at end of file diff --git a/src/pay/utils/hydrateCharge.ts b/src/pay/utils/hydrateCharge.ts new file mode 100644 index 000000000..6e88f83d2 --- /dev/null +++ b/src/pay/utils/hydrateCharge.ts @@ -0,0 +1,45 @@ +import { sendRequest } from "../../network/request"; +import type { HydrateChargeAPIResponse } from "../types"; +import { getPayErrorCode } from "./getPayErrorCode"; + +type HydrateChargeParams = { + sender: `0x{string}`; + chargeId: string; +}; + +type HydrateChargeAPIParams = { + sender: `0x{string}`; + chainId: number; + chargeId: string; +}; + +const HYDRATE_CHARGE_METHOD_NAME = "pay_hydrateCharge"; + +export async function hydrateCharge({ sender, chargeId }: HydrateChargeParams) { + try { + const res = await sendRequest< + HydrateChargeAPIParams, + HydrateChargeAPIResponse + >(HYDRATE_CHARGE_METHOD_NAME, [ + { + sender: sender, + chainId: 8453, + chargeId, + }, + ]); + if (res.error) { + return { + code: getPayErrorCode(res.error?.code), + error: res.error.message, + message: "", + }; + } + return res.result; + } catch (_error) { + return { + code: getPayErrorCode(), + error: "Something went wrong", + message: "", + }; + } +} From c268fb1a1d4fccddab7db39dcc69d4ec74cd3742 Mon Sep 17 00:00:00 2001 From: Andrew Reder Date: Wed, 28 Aug 2024 12:27:06 -0700 Subject: [PATCH 2/8] update error codes --- src/pay/constants.ts | 7 +++--- src/pay/utils/getPayErrorCode.ts | 37 +++++++++++++++++++------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/pay/constants.ts b/src/pay/constants.ts index c36655f15..e5e294f13 100644 --- a/src/pay/constants.ts +++ b/src/pay/constants.ts @@ -1,3 +1,4 @@ -export const GENERAL_PAY_ERROR_CODE = "PAY_ERROR" -export const PAY_TOO_MANY_REQUESTS_ERROR_CODE = 'TOO_MANY_REQUESTS_ERROR'; -export const PAY_INVALID_PARAMETER_ERROR_CODE = "PAY_INVALID_PARAMETER_ERROR" \ No newline at end of file +export const GENERAL_PAY_ERROR_CODE = "PAY_ERROR"; +export const PAY_TOO_MANY_REQUESTS_ERROR_CODE = "PAY_TOO_MANY_REQUESTS_ERROR"; +export const PAY_INVALID_CHARGE_ERROR_CODE = "PAY_INVALID_CHARGE_ERROR"; +export const PAY_INVALID_PARAMETER_ERROR_CODE = "PAY_INVALID_PARAMETER_ERROR"; diff --git a/src/pay/utils/getPayErrorCode.ts b/src/pay/utils/getPayErrorCode.ts index deb58309a..09e1bd989 100644 --- a/src/pay/utils/getPayErrorCode.ts +++ b/src/pay/utils/getPayErrorCode.ts @@ -1,15 +1,22 @@ -import { GENERAL_PAY_ERROR_CODE, PAY_INVALID_PARAMETER_ERROR_CODE, PAY_TOO_MANY_REQUESTS_ERROR_CODE } from "../constants"; - -export function getPayErrorCode( - errorCode?: number, - ) { - if (errorCode === -32001) { - return PAY_TOO_MANY_REQUESTS_ERROR_CODE; - } - - if (errorCode === -32602) { - return PAY_INVALID_PARAMETER_ERROR_CODE; - } - - return GENERAL_PAY_ERROR_CODE; - } \ No newline at end of file +import { + GENERAL_PAY_ERROR_CODE, + PAY_INVALID_CHARGE_ERROR_CODE, + PAY_INVALID_PARAMETER_ERROR_CODE, + PAY_TOO_MANY_REQUESTS_ERROR_CODE, +} from "../constants"; + +export function getPayErrorCode(errorCode?: number) { + if (errorCode === -32001) { + return PAY_TOO_MANY_REQUESTS_ERROR_CODE; + } + + if (errorCode === -32601) { + return PAY_INVALID_CHARGE_ERROR_CODE; + } + + if (errorCode === -32602) { + return PAY_INVALID_PARAMETER_ERROR_CODE; + } + + return GENERAL_PAY_ERROR_CODE; +} From aca460d531157ac72b9057e39212e4371ce05c22 Mon Sep 17 00:00:00 2001 From: Andrew Reder Date: Thu, 29 Aug 2024 06:01:17 -0700 Subject: [PATCH 3/8] restructure hydration utility; update types --- src/api/hydrateCharge.ts | 40 ++++++++++++++++++++++++++++ src/api/types.ts | 20 ++++++++++++++ src/network/definitions/pay.ts | 1 + src/pay/constants.ts | 8 +++--- src/pay/types.ts | 22 +++++++++------- src/pay/utils/getPayErrorCode.ts | 2 +- src/pay/utils/hydrateCharge.ts | 45 -------------------------------- 7 files changed, 78 insertions(+), 60 deletions(-) create mode 100644 src/api/hydrateCharge.ts create mode 100644 src/network/definitions/pay.ts delete mode 100644 src/pay/utils/hydrateCharge.ts diff --git a/src/api/hydrateCharge.ts b/src/api/hydrateCharge.ts new file mode 100644 index 000000000..b6a021e7a --- /dev/null +++ b/src/api/hydrateCharge.ts @@ -0,0 +1,40 @@ +import { PAY_HYDRATE_CHARGE } from '../network/definitions/pay'; +import { sendRequest } from '../network/request'; +import { getPayErrorCode } from '../pay/utils/getPayErrorCode'; +import type { + HydrateChargeAPIParams, + HydrateChargeParams, + HydrateChargeResponse, +} from './types'; + +export async function hydrateCharge({ + sender, + chargeId, +}: HydrateChargeParams): Promise { + try { + const res = await sendRequest< + HydrateChargeAPIParams, + HydrateChargeResponse + >(PAY_HYDRATE_CHARGE, [ + { + sender: sender, + chainId: 8453, + chargeId, + }, + ]); + if (res.error) { + return { + code: getPayErrorCode(res.error?.code), + error: res.error.message, + message: '', + }; + } + return res.result; + } catch (_error) { + return { + code: getPayErrorCode(), + error: 'Something went wrong', + message: '', + }; + } +} diff --git a/src/api/types.ts b/src/api/types.ts index 121224540..c43ea7d22 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,4 +1,5 @@ import type { Address } from 'viem'; +import type { HydratedCharge } from '../pay/types'; import type { SwapQuote } from '../swap/types'; import type { Token } from '../token/types'; @@ -79,3 +80,22 @@ export type RawTransactionData = { }; export type SwapAPIParams = GetQuoteAPIParams | GetSwapAPIParams; + +/** + * Note: exported as public Type + */ +export type HydrateChargeParams = { + sender: Address; // The address of the wallet paying + chargeId: string; // The ID of the Commerce Charge to be paid +}; + +export type HydrateChargeAPIParams = { + sender: Address; // The address of the wallet paying + chainId: 8453; // The Chain ID of the payment Network (only Base is supported) + chargeId: string; // The ID of the Commerce Charge to be paid +}; + +/** + * Note: exported as public Type + */ +export type HydrateChargeResponse = HydratedCharge | APIError; diff --git a/src/network/definitions/pay.ts b/src/network/definitions/pay.ts new file mode 100644 index 000000000..022ce66ab --- /dev/null +++ b/src/network/definitions/pay.ts @@ -0,0 +1 @@ +export const PAY_HYDRATE_CHARGE = 'pay_hydrateCharge'; diff --git a/src/pay/constants.ts b/src/pay/constants.ts index e5e294f13..94c703960 100644 --- a/src/pay/constants.ts +++ b/src/pay/constants.ts @@ -1,4 +1,4 @@ -export const GENERAL_PAY_ERROR_CODE = "PAY_ERROR"; -export const PAY_TOO_MANY_REQUESTS_ERROR_CODE = "PAY_TOO_MANY_REQUESTS_ERROR"; -export const PAY_INVALID_CHARGE_ERROR_CODE = "PAY_INVALID_CHARGE_ERROR"; -export const PAY_INVALID_PARAMETER_ERROR_CODE = "PAY_INVALID_PARAMETER_ERROR"; +export const GENERAL_PAY_ERROR_CODE = 'PAY_ERROR'; +export const PAY_TOO_MANY_REQUESTS_ERROR_CODE = 'PAY_TOO_MANY_REQUESTS_ERROR'; +export const PAY_INVALID_CHARGE_ERROR_CODE = 'PAY_INVALID_CHARGE_ERROR'; +export const PAY_INVALID_PARAMETER_ERROR_CODE = 'PAY_INVALID_PARAMETER_ERROR'; diff --git a/src/pay/types.ts b/src/pay/types.ts index 5c7a18445..3f0648956 100644 --- a/src/pay/types.ts +++ b/src/pay/types.ts @@ -1,21 +1,23 @@ -export type HydrateChargeAPIResponse = { +import type { Address } from 'viem'; + +export type HydratedCharge = { id: string; callData: { deadline: string; feeAmount: string; id: string; - operator: `0x${string}`; - prefix: `0x${string}`; - recipient: `0x${string}`; + operator: Address; + prefix: Address; + recipient: Address; recipientAmount: string; - recipientCurrency: `0x${string}`; - refundDestination: `0x${string}`; - signature: `0x${string}`; + recipientCurrency: Address; + refundDestination: Address; + signature: Address; }; metaData: { chainId: number; - contractAddress: `0x${string}`; - sender: `0x${string}`; - settlementCurrencyAddress: `0x${string}`; + contractAddress: Address; + sender: Address; + settlementCurrencyAddress: Address; }; }; diff --git a/src/pay/utils/getPayErrorCode.ts b/src/pay/utils/getPayErrorCode.ts index 09e1bd989..f40558424 100644 --- a/src/pay/utils/getPayErrorCode.ts +++ b/src/pay/utils/getPayErrorCode.ts @@ -3,7 +3,7 @@ import { PAY_INVALID_CHARGE_ERROR_CODE, PAY_INVALID_PARAMETER_ERROR_CODE, PAY_TOO_MANY_REQUESTS_ERROR_CODE, -} from "../constants"; +} from '../constants'; export function getPayErrorCode(errorCode?: number) { if (errorCode === -32001) { diff --git a/src/pay/utils/hydrateCharge.ts b/src/pay/utils/hydrateCharge.ts deleted file mode 100644 index 6e88f83d2..000000000 --- a/src/pay/utils/hydrateCharge.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { sendRequest } from "../../network/request"; -import type { HydrateChargeAPIResponse } from "../types"; -import { getPayErrorCode } from "./getPayErrorCode"; - -type HydrateChargeParams = { - sender: `0x{string}`; - chargeId: string; -}; - -type HydrateChargeAPIParams = { - sender: `0x{string}`; - chainId: number; - chargeId: string; -}; - -const HYDRATE_CHARGE_METHOD_NAME = "pay_hydrateCharge"; - -export async function hydrateCharge({ sender, chargeId }: HydrateChargeParams) { - try { - const res = await sendRequest< - HydrateChargeAPIParams, - HydrateChargeAPIResponse - >(HYDRATE_CHARGE_METHOD_NAME, [ - { - sender: sender, - chainId: 8453, - chargeId, - }, - ]); - if (res.error) { - return { - code: getPayErrorCode(res.error?.code), - error: res.error.message, - message: "", - }; - } - return res.result; - } catch (_error) { - return { - code: getPayErrorCode(), - error: "Something went wrong", - message: "", - }; - } -} From 0f2ef02c75003596eef380e37804d51f9af12f78 Mon Sep 17 00:00:00 2001 From: Andrew Reder Date: Thu, 29 Aug 2024 08:38:49 -0700 Subject: [PATCH 4/8] add tests; improve error handling --- src/api/hydrateCharge.test.ts | 153 ++++++++++++++++++++++++++++++++++ src/api/hydrateCharge.ts | 3 +- src/pay/constants.ts | 2 + 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/api/hydrateCharge.test.ts diff --git a/src/api/hydrateCharge.test.ts b/src/api/hydrateCharge.test.ts new file mode 100644 index 000000000..49cbf8648 --- /dev/null +++ b/src/api/hydrateCharge.test.ts @@ -0,0 +1,153 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { PAY_HYDRATE_CHARGE } from '../network/definitions/pay'; +import { sendRequest } from '../network/request'; +import { + PAY_INVALID_CHARGE_ERROR_CODE, + UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE, +} from '../pay/constants'; +/** + * @vitest-environment node + */ +import { hydrateCharge } from './hydrateCharge'; +import type { HydrateChargeAPIParams, HydrateChargeParams } from './types'; + +vi.mock('../network/request'); + +const testCharge = '1b03e80d-4e87-46fd-9772-422a1b693fb7'; +const testSender = '0x98fAbEA34A3A377916EBF7793f37E11EE98D29Eb'; + +describe('hydrateCharge', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should return a a hydrated charge', async () => { + const mockParams: HydrateChargeParams = { + sender: testSender, + chargeId: testCharge, + }; + const mockAPIParams: HydrateChargeAPIParams = { + ...mockParams, + chainId: 8453, + }; + const mockResponse = { + id: 1, + jsonrpc: '2.0', + result: { + id: testCharge, + callData: { + deadline: '2024-08-29T23:00:38Z', + feeAmount: '10000', + id: '0xd2e57fb373f246768a193cadd4a5ce1e', + operator: '0xd1db362f9d23a029834375afa2b37d91d2e67a95', + prefix: + '0x4b3220496e666f726d6174696f6e616c204d6573736167653a20333220', + recipient: '0xb724dcF5f1156dd8E2AB217921b5Bd46a9e5cAa5', + recipientAmount: '990000', + recipientCurrency: '0xF175520C52418dfE19C8098071a252da48Cd1C19', + refundDestination: testSender, + signature: + '0xb49a08026bdfdc55e3b1b797a9481fbdb7a9246c73f5f77fece76d5f24e979561f2168862aed0dd72980a4f9930cf23836084f3326b98b5546b280c4f0d57aae1b', + }, + metaData: { + chainId: 8453, + contractAddress: '0x131642c019AF815Ae5F9926272A70C84AE5C37ab', + sender: testSender, + settlementCurrencyAddress: + '0xF175520C52418dfE19C8098071a252da48Cd1C19', + }, + }, + }; + (sendRequest as vi.Mock).mockResolvedValue(mockResponse); + const hydratedCharge = await hydrateCharge(mockParams); + expect(hydratedCharge).toEqual(mockResponse.result); + expect(sendRequest).toHaveBeenCalledTimes(1); + expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ + mockAPIParams, + ]); + }); + + it('should return an error if sendRequest fails', async () => { + const mockParams: HydrateChargeParams = { + sender: testSender, + chargeId: testCharge, + }; + const mockAPIParams: HydrateChargeAPIParams = { + ...mockParams, + chainId: 8453, + }; + const mockResponse = { + id: 1, + jsonrpc: '2.0', + result: { + id: testCharge, + callData: { + deadline: '2024-08-29T23:00:38Z', + feeAmount: '10000', + id: '0xd2e57fb373f246768a193cadd4a5ce1e', + operator: '0xd1db362f9d23a029834375afa2b37d91d2e67a95', + prefix: + '0x4b3220496e666f726d6174696f6e616c204d6573736167653a20333220', + recipient: '0xb724dcF5f1156dd8E2AB217921b5Bd46a9e5cAa5', + recipientAmount: '990000', + recipientCurrency: '0xF175520C52418dfE19C8098071a252da48Cd1C19', + refundDestination: testSender, + signature: + '0xb49a08026bdfdc55e3b1b797a9481fbdb7a9246c73f5f77fece76d5f24e979561f2168862aed0dd72980a4f9930cf23836084f3326b98b5546b280c4f0d57aae1b', + }, + metaData: { + chainId: 8453, + contractAddress: '0x131642c019AF815Ae5F9926272A70C84AE5C37ab', + sender: testSender, + settlementCurrencyAddress: + '0xF175520C52418dfE19C8098071a252da48Cd1C19', + }, + }, + }; + (sendRequest as vi.Mock).mockResolvedValue(mockResponse); + const hydratedCharge = await hydrateCharge(mockParams); + expect(hydratedCharge).toEqual(mockResponse.result); + expect(sendRequest).toHaveBeenCalledTimes(1); + expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ + mockAPIParams, + ]); + const mockError = new Error('hydrateCharge: Error: Failed to send request'); + (sendRequest as vi.Mock).mockRejectedValue(mockError); + const error = await hydrateCharge(mockParams); + expect(error).toEqual({ + code: UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE, + error: 'Something went wrong', + message: '', + }); + }); + + it('should return an error object from hydrateCharge', async () => { + const mockParams: HydrateChargeParams = { + sender: testSender, + chargeId: testCharge, + }; + const mockAPIParams: HydrateChargeAPIParams = { + ...mockParams, + chainId: 8453, + }; + const mockResponse = { + id: 1, + jsonrpc: '2.0', + error: { + code: -32601, + message: 'method not found - Not found', + }, + }; + (sendRequest as vi.Mock).mockResolvedValue(mockResponse); + const error = await hydrateCharge(mockParams); + expect(error).toEqual({ + code: PAY_INVALID_CHARGE_ERROR_CODE, + error: 'method not found - Not found', + message: '', + }); + expect(sendRequest).toHaveBeenCalledTimes(1); + expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ + mockAPIParams, + ]); + }); +}); diff --git a/src/api/hydrateCharge.ts b/src/api/hydrateCharge.ts index b6a021e7a..356020585 100644 --- a/src/api/hydrateCharge.ts +++ b/src/api/hydrateCharge.ts @@ -1,5 +1,6 @@ import { PAY_HYDRATE_CHARGE } from '../network/definitions/pay'; import { sendRequest } from '../network/request'; +import { UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE } from '../pay/constants'; import { getPayErrorCode } from '../pay/utils/getPayErrorCode'; import type { HydrateChargeAPIParams, @@ -32,7 +33,7 @@ export async function hydrateCharge({ return res.result; } catch (_error) { return { - code: getPayErrorCode(), + code: UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE, error: 'Something went wrong', message: '', }; diff --git a/src/pay/constants.ts b/src/pay/constants.ts index 94c703960..7dcc970cf 100644 --- a/src/pay/constants.ts +++ b/src/pay/constants.ts @@ -2,3 +2,5 @@ export const GENERAL_PAY_ERROR_CODE = 'PAY_ERROR'; export const PAY_TOO_MANY_REQUESTS_ERROR_CODE = 'PAY_TOO_MANY_REQUESTS_ERROR'; export const PAY_INVALID_CHARGE_ERROR_CODE = 'PAY_INVALID_CHARGE_ERROR'; export const PAY_INVALID_PARAMETER_ERROR_CODE = 'PAY_INVALID_PARAMETER_ERROR'; +export const UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE = + 'UNCAUGHT_HYDRATE_CHARGE_ERROR'; From e7232f7b64ba1cbc66f68dde28dfe342a642b1a1 Mon Sep 17 00:00:00 2001 From: Andrew Reder Date: Thu, 29 Aug 2024 09:52:13 -0700 Subject: [PATCH 5/8] additional tests for error codes handling --- src/pay/utils/getPayErrorCode.test.ts | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/pay/utils/getPayErrorCode.test.ts diff --git a/src/pay/utils/getPayErrorCode.test.ts b/src/pay/utils/getPayErrorCode.test.ts new file mode 100644 index 000000000..48b08b33a --- /dev/null +++ b/src/pay/utils/getPayErrorCode.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest'; +import { + GENERAL_PAY_ERROR_CODE, + PAY_INVALID_CHARGE_ERROR_CODE, + PAY_INVALID_PARAMETER_ERROR_CODE, + PAY_TOO_MANY_REQUESTS_ERROR_CODE, +} from '../constants'; +import { getPayErrorCode } from './getPayErrorCode'; + +describe('getPayErrorCode', () => { + it('should return TOO_MANY_REQUESTS_ERROR_CODE for errorCode -32001', () => { + const result = getPayErrorCode(-32001); + expect(result).toBe(PAY_TOO_MANY_REQUESTS_ERROR_CODE); + }); + + it('should return PAY_INVALID_CHARGE_ERROR_CODE for errorCode -32601', () => { + const result = getPayErrorCode(-32601); + expect(result).toBe(PAY_INVALID_CHARGE_ERROR_CODE); + }); + + it('should return PAY_INVALID_PARAMETER_ERROR_CODE for errorCode -32602', () => { + const result = getPayErrorCode(-32602); + expect(result).toBe(PAY_INVALID_PARAMETER_ERROR_CODE); + }); + + it('should return GENERAL_PAY_ERROR_CODE for misc errorCode', () => { + const result = getPayErrorCode(-32603); + expect(result).toBe(GENERAL_PAY_ERROR_CODE); + }); + + it('should return GENERAL_PAY_ERROR_CODE for no errorCode', () => { + const result = getPayErrorCode(); + expect(result).toBe(GENERAL_PAY_ERROR_CODE); + }); +}); From 73b89862c1d67152d8f72ba61996ec1f781efcf5 Mon Sep 17 00:00:00 2001 From: Andrew Reder Date: Fri, 30 Aug 2024 05:40:38 -0700 Subject: [PATCH 6/8] rename to buildPayTransaction; update error handling with location codes --- src/api/buildPayTransaction.ts | 50 ++++++ src/api/bulidPayTransaction.test.ts | 134 +++++++++++++++ .../fixtures/buildPayTransactionFixtures.ts | 38 +++++ src/api/hydrateCharge.test.ts | 153 ------------------ src/api/hydrateCharge.ts | 41 ----- src/api/types.ts | 32 +++- src/pay/constants.ts | 14 +- src/pay/types.ts | 23 --- src/pay/utils/getPayErrorCode.test.ts | 35 ---- src/pay/utils/getPayErrorCode.ts | 22 --- src/pay/utils/getPayErrorMessage.test.ts | 36 +++++ src/pay/utils/getPayErrorMessage.ts | 27 ++++ 12 files changed, 320 insertions(+), 285 deletions(-) create mode 100644 src/api/buildPayTransaction.ts create mode 100644 src/api/bulidPayTransaction.test.ts create mode 100644 src/api/fixtures/buildPayTransactionFixtures.ts delete mode 100644 src/api/hydrateCharge.test.ts delete mode 100644 src/api/hydrateCharge.ts delete mode 100644 src/pay/types.ts delete mode 100644 src/pay/utils/getPayErrorCode.test.ts delete mode 100644 src/pay/utils/getPayErrorCode.ts create mode 100644 src/pay/utils/getPayErrorMessage.test.ts create mode 100644 src/pay/utils/getPayErrorMessage.ts diff --git a/src/api/buildPayTransaction.ts b/src/api/buildPayTransaction.ts new file mode 100644 index 000000000..26853d713 --- /dev/null +++ b/src/api/buildPayTransaction.ts @@ -0,0 +1,50 @@ +import { base } from 'viem/chains'; +import { PAY_HYDRATE_CHARGE } from '../network/definitions/pay'; +import { sendRequest } from '../network/request'; +import { PAY_UNSUPPORTED_CHAIN_ERROR_MESSAGE } from '../pay/constants'; +import { getPayErrorMessage } from '../pay/utils/getPayErrorMessage'; +import type { + BuildPayTransactionParams, + BuildPayTransactionResponse, + HydrateChargeAPIParams, +} from './types'; + +export async function buildPayTransaction({ + payerAddress, + chainId, + chargeId, +}: BuildPayTransactionParams): Promise { + if (chainId !== base.id) { + return { + code: 'AmBPT01', // Api Module Build Pay Transaction Error 01 + error: 'Pay Transactions must be on Base', + message: PAY_UNSUPPORTED_CHAIN_ERROR_MESSAGE, + }; + } + try { + const res = await sendRequest< + HydrateChargeAPIParams, + BuildPayTransactionResponse + >(PAY_HYDRATE_CHARGE, [ + { + sender: payerAddress, + chainId: chainId, + chargeId, + }, + ]); + if (res.error) { + return { + code: 'AmBPT02', // Api Module Build Pay Transaction Error 02 + error: res.error.message, + message: getPayErrorMessage(res.error?.code), + }; + } + return res.result; + } catch (_error) { + return { + code: 'AmBPT03', // Api Module Build Pay Transaction Error 03 + error: 'Something went wrong', + message: getPayErrorMessage(), + }; + } +} diff --git a/src/api/bulidPayTransaction.test.ts b/src/api/bulidPayTransaction.test.ts new file mode 100644 index 000000000..a13463f67 --- /dev/null +++ b/src/api/bulidPayTransaction.test.ts @@ -0,0 +1,134 @@ +import { base, mainnet } from 'viem/chains'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import type { Mock } from 'vitest'; +import { PAY_HYDRATE_CHARGE } from '../network/definitions/pay'; +import { sendRequest } from '../network/request'; +import { + PAY_INVALID_CHARGE_ERROR_MESSAGE, + PAY_UNSUPPORTED_CHAIN_ERROR_MESSAGE, + UNCAUGHT_PAY_ERROR_MESSAGE, +} from '../pay/constants'; +/** + * @vitest-environment node + */ +import { buildPayTransaction } from './buildPayTransaction'; +import { + MOCK_HYDRATE_CHARGE_INVALID_CHARGE_ERROR_RESPONSE, + MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE, + MOCK_INVALID_CHARGE_ID, + MOCK_VALID_CHARGE_ID, + MOCK_VALID_PAYER_ADDRESS, +} from './fixtures/buildPayTransactionFixtures'; +import type { + BuildPayTransactionParams, + HydrateChargeAPIParams, +} from './types'; + +vi.mock('../network/request'); + +describe('buildPayTransaction', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should return a Pay Transaction', async () => { + const mockParams: BuildPayTransactionParams = { + payerAddress: MOCK_VALID_PAYER_ADDRESS, + chainId: base.id, + chargeId: MOCK_VALID_CHARGE_ID, + }; + const mockAPIParams: HydrateChargeAPIParams = { + sender: MOCK_VALID_PAYER_ADDRESS, + chargeId: MOCK_VALID_CHARGE_ID, + chainId: base.id, + }; + + (sendRequest as Mock).mockResolvedValue( + MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE, + ); + const payTransaction = await buildPayTransaction(mockParams); + expect(payTransaction).toEqual(MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE.result); + expect(sendRequest).toHaveBeenCalledTimes(1); + expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ + mockAPIParams, + ]); + }); + + it('should return an error for chains other than Base', async () => { + const mockParams: BuildPayTransactionParams = { + payerAddress: MOCK_VALID_PAYER_ADDRESS, + chainId: mainnet.id, + chargeId: MOCK_VALID_CHARGE_ID, + }; + + (sendRequest as Mock).mockResolvedValue( + MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE, + ); + const error = await buildPayTransaction(mockParams); + expect(error).toEqual({ + code: 'AmBPT01', + error: 'Pay Transactions must be on Base', + message: PAY_UNSUPPORTED_CHAIN_ERROR_MESSAGE, + }); + expect(sendRequest).not.toHaveBeenCalled(); + }); + + it('should return an error if sendRequest fails', async () => { + const mockParams: BuildPayTransactionParams = { + payerAddress: MOCK_VALID_PAYER_ADDRESS, + chainId: base.id, + chargeId: MOCK_VALID_CHARGE_ID, + }; + const mockAPIParams: HydrateChargeAPIParams = { + sender: MOCK_VALID_PAYER_ADDRESS, + chargeId: MOCK_VALID_CHARGE_ID, + chainId: base.id, + }; + + (sendRequest as Mock).mockResolvedValue( + MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE, + ); + const hydratedCharge = await buildPayTransaction(mockParams); + expect(hydratedCharge).toEqual(MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE.result); + expect(sendRequest).toHaveBeenCalledTimes(1); + expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ + mockAPIParams, + ]); + const mockError = new Error( + 'buildPayTransaction: Error: Failed to send request', + ); + (sendRequest as Mock).mockRejectedValue(mockError); + const error = await buildPayTransaction(mockParams); + expect(error).toEqual({ + code: 'AmBPT03', + error: 'Something went wrong', + message: UNCAUGHT_PAY_ERROR_MESSAGE, + }); + }); + + it('should return an error object from buildPayTransaction', async () => { + const mockParams: BuildPayTransactionParams = { + payerAddress: MOCK_VALID_PAYER_ADDRESS, + chainId: base.id, + chargeId: MOCK_INVALID_CHARGE_ID, + }; + const mockAPIParams: HydrateChargeAPIParams = { + sender: MOCK_VALID_PAYER_ADDRESS, + chargeId: MOCK_INVALID_CHARGE_ID, + chainId: base.id, + }; + (sendRequest as Mock).mockResolvedValue( + MOCK_HYDRATE_CHARGE_INVALID_CHARGE_ERROR_RESPONSE, + ); + const error = await buildPayTransaction(mockParams); + expect(error).toEqual({ + code: 'AmBPT02', + error: 'method not found - Not found', + message: PAY_INVALID_CHARGE_ERROR_MESSAGE, + }); + expect(sendRequest).toHaveBeenCalledTimes(1); + expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ + mockAPIParams, + ]); + }); +}); diff --git a/src/api/fixtures/buildPayTransactionFixtures.ts b/src/api/fixtures/buildPayTransactionFixtures.ts new file mode 100644 index 000000000..7457c43ca --- /dev/null +++ b/src/api/fixtures/buildPayTransactionFixtures.ts @@ -0,0 +1,38 @@ +export const MOCK_VALID_CHARGE_ID = '1b03e80d-4e87-46fd-9772-422a1b693fb7'; +export const MOCK_INVALID_CHARGE_ID = '00000000-0000-0000-0000-000000000000'; +export const MOCK_VALID_PAYER_ADDRESS = + '0x98fAbEA34A3A377916EBF7793f37E11EE98D29Eb'; +export const MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE = { + id: 1, + jsonrpc: '2.0', + result: { + id: MOCK_VALID_CHARGE_ID, + callData: { + deadline: '2024-08-29T23:00:38Z', + feeAmount: '10000', + id: '0xd2e57fb373f246768a193cadd4a5ce1e', + operator: '0xd1db362f9d23a029834375afa2b37d91d2e67a95', + prefix: '0x4b3220496e666f726d6174696f6e616c204d6573736167653a20333220', + recipient: '0xb724dcF5f1156dd8E2AB217921b5Bd46a9e5cAa5', + recipientAmount: '990000', + recipientCurrency: '0xF175520C52418dfE19C8098071a252da48Cd1C19', + refundDestination: MOCK_VALID_PAYER_ADDRESS, + signature: + '0xb49a08026bdfdc55e3b1b797a9481fbdb7a9246c73f5f77fece76d5f24e979561f2168862aed0dd72980a4f9930cf23836084f3326b98b5546b280c4f0d57aae1b', + }, + metaData: { + chainId: 8453, + contractAddress: '0x131642c019AF815Ae5F9926272A70C84AE5C37ab', + sender: MOCK_VALID_PAYER_ADDRESS, + settlementCurrencyAddress: '0xF175520C52418dfE19C8098071a252da48Cd1C19', + }, + }, +}; +export const MOCK_HYDRATE_CHARGE_INVALID_CHARGE_ERROR_RESPONSE = { + id: 1, + jsonrpc: '2.0', + error: { + code: -32601, + message: 'method not found - Not found', + }, +}; diff --git a/src/api/hydrateCharge.test.ts b/src/api/hydrateCharge.test.ts deleted file mode 100644 index 49cbf8648..000000000 --- a/src/api/hydrateCharge.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest'; -import { PAY_HYDRATE_CHARGE } from '../network/definitions/pay'; -import { sendRequest } from '../network/request'; -import { - PAY_INVALID_CHARGE_ERROR_CODE, - UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE, -} from '../pay/constants'; -/** - * @vitest-environment node - */ -import { hydrateCharge } from './hydrateCharge'; -import type { HydrateChargeAPIParams, HydrateChargeParams } from './types'; - -vi.mock('../network/request'); - -const testCharge = '1b03e80d-4e87-46fd-9772-422a1b693fb7'; -const testSender = '0x98fAbEA34A3A377916EBF7793f37E11EE98D29Eb'; - -describe('hydrateCharge', () => { - afterEach(() => { - vi.clearAllMocks(); - }); - - it('should return a a hydrated charge', async () => { - const mockParams: HydrateChargeParams = { - sender: testSender, - chargeId: testCharge, - }; - const mockAPIParams: HydrateChargeAPIParams = { - ...mockParams, - chainId: 8453, - }; - const mockResponse = { - id: 1, - jsonrpc: '2.0', - result: { - id: testCharge, - callData: { - deadline: '2024-08-29T23:00:38Z', - feeAmount: '10000', - id: '0xd2e57fb373f246768a193cadd4a5ce1e', - operator: '0xd1db362f9d23a029834375afa2b37d91d2e67a95', - prefix: - '0x4b3220496e666f726d6174696f6e616c204d6573736167653a20333220', - recipient: '0xb724dcF5f1156dd8E2AB217921b5Bd46a9e5cAa5', - recipientAmount: '990000', - recipientCurrency: '0xF175520C52418dfE19C8098071a252da48Cd1C19', - refundDestination: testSender, - signature: - '0xb49a08026bdfdc55e3b1b797a9481fbdb7a9246c73f5f77fece76d5f24e979561f2168862aed0dd72980a4f9930cf23836084f3326b98b5546b280c4f0d57aae1b', - }, - metaData: { - chainId: 8453, - contractAddress: '0x131642c019AF815Ae5F9926272A70C84AE5C37ab', - sender: testSender, - settlementCurrencyAddress: - '0xF175520C52418dfE19C8098071a252da48Cd1C19', - }, - }, - }; - (sendRequest as vi.Mock).mockResolvedValue(mockResponse); - const hydratedCharge = await hydrateCharge(mockParams); - expect(hydratedCharge).toEqual(mockResponse.result); - expect(sendRequest).toHaveBeenCalledTimes(1); - expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ - mockAPIParams, - ]); - }); - - it('should return an error if sendRequest fails', async () => { - const mockParams: HydrateChargeParams = { - sender: testSender, - chargeId: testCharge, - }; - const mockAPIParams: HydrateChargeAPIParams = { - ...mockParams, - chainId: 8453, - }; - const mockResponse = { - id: 1, - jsonrpc: '2.0', - result: { - id: testCharge, - callData: { - deadline: '2024-08-29T23:00:38Z', - feeAmount: '10000', - id: '0xd2e57fb373f246768a193cadd4a5ce1e', - operator: '0xd1db362f9d23a029834375afa2b37d91d2e67a95', - prefix: - '0x4b3220496e666f726d6174696f6e616c204d6573736167653a20333220', - recipient: '0xb724dcF5f1156dd8E2AB217921b5Bd46a9e5cAa5', - recipientAmount: '990000', - recipientCurrency: '0xF175520C52418dfE19C8098071a252da48Cd1C19', - refundDestination: testSender, - signature: - '0xb49a08026bdfdc55e3b1b797a9481fbdb7a9246c73f5f77fece76d5f24e979561f2168862aed0dd72980a4f9930cf23836084f3326b98b5546b280c4f0d57aae1b', - }, - metaData: { - chainId: 8453, - contractAddress: '0x131642c019AF815Ae5F9926272A70C84AE5C37ab', - sender: testSender, - settlementCurrencyAddress: - '0xF175520C52418dfE19C8098071a252da48Cd1C19', - }, - }, - }; - (sendRequest as vi.Mock).mockResolvedValue(mockResponse); - const hydratedCharge = await hydrateCharge(mockParams); - expect(hydratedCharge).toEqual(mockResponse.result); - expect(sendRequest).toHaveBeenCalledTimes(1); - expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ - mockAPIParams, - ]); - const mockError = new Error('hydrateCharge: Error: Failed to send request'); - (sendRequest as vi.Mock).mockRejectedValue(mockError); - const error = await hydrateCharge(mockParams); - expect(error).toEqual({ - code: UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE, - error: 'Something went wrong', - message: '', - }); - }); - - it('should return an error object from hydrateCharge', async () => { - const mockParams: HydrateChargeParams = { - sender: testSender, - chargeId: testCharge, - }; - const mockAPIParams: HydrateChargeAPIParams = { - ...mockParams, - chainId: 8453, - }; - const mockResponse = { - id: 1, - jsonrpc: '2.0', - error: { - code: -32601, - message: 'method not found - Not found', - }, - }; - (sendRequest as vi.Mock).mockResolvedValue(mockResponse); - const error = await hydrateCharge(mockParams); - expect(error).toEqual({ - code: PAY_INVALID_CHARGE_ERROR_CODE, - error: 'method not found - Not found', - message: '', - }); - expect(sendRequest).toHaveBeenCalledTimes(1); - expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ - mockAPIParams, - ]); - }); -}); diff --git a/src/api/hydrateCharge.ts b/src/api/hydrateCharge.ts deleted file mode 100644 index 356020585..000000000 --- a/src/api/hydrateCharge.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { PAY_HYDRATE_CHARGE } from '../network/definitions/pay'; -import { sendRequest } from '../network/request'; -import { UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE } from '../pay/constants'; -import { getPayErrorCode } from '../pay/utils/getPayErrorCode'; -import type { - HydrateChargeAPIParams, - HydrateChargeParams, - HydrateChargeResponse, -} from './types'; - -export async function hydrateCharge({ - sender, - chargeId, -}: HydrateChargeParams): Promise { - try { - const res = await sendRequest< - HydrateChargeAPIParams, - HydrateChargeResponse - >(PAY_HYDRATE_CHARGE, [ - { - sender: sender, - chainId: 8453, - chargeId, - }, - ]); - if (res.error) { - return { - code: getPayErrorCode(res.error?.code), - error: res.error.message, - message: '', - }; - } - return res.result; - } catch (_error) { - return { - code: UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE, - error: 'Something went wrong', - message: '', - }; - } -} diff --git a/src/api/types.ts b/src/api/types.ts index c43ea7d22..1c079b7fb 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,5 +1,4 @@ import type { Address } from 'viem'; -import type { HydratedCharge } from '../pay/types'; import type { SwapQuote } from '../swap/types'; import type { Token } from '../token/types'; @@ -84,18 +83,41 @@ export type SwapAPIParams = GetQuoteAPIParams | GetSwapAPIParams; /** * Note: exported as public Type */ -export type HydrateChargeParams = { - sender: Address; // The address of the wallet paying +export type BuildPayTransactionParams = { + payerAddress: Address; // The address of the wallet paying + chainId: number; // The Chain ID of the payment Network (only Base is supported) chargeId: string; // The ID of the Commerce Charge to be paid }; export type HydrateChargeAPIParams = { sender: Address; // The address of the wallet paying - chainId: 8453; // The Chain ID of the payment Network (only Base is supported) + chainId: number; // The Chain ID of the payment Network (only Base is supported) chargeId: string; // The ID of the Commerce Charge to be paid }; +export type HydratedCharge = { + id: string; + callData: { + deadline: string; + feeAmount: string; + id: string; + operator: Address; + prefix: Address; + recipient: Address; + recipientAmount: string; + recipientCurrency: Address; + refundDestination: Address; + signature: Address; + }; + metaData: { + chainId: number; + contractAddress: Address; + sender: Address; + settlementCurrencyAddress: Address; + }; +}; + /** * Note: exported as public Type */ -export type HydrateChargeResponse = HydratedCharge | APIError; +export type BuildPayTransactionResponse = HydratedCharge | APIError; diff --git a/src/pay/constants.ts b/src/pay/constants.ts index 7dcc970cf..0a820e71d 100644 --- a/src/pay/constants.ts +++ b/src/pay/constants.ts @@ -1,6 +1,8 @@ -export const GENERAL_PAY_ERROR_CODE = 'PAY_ERROR'; -export const PAY_TOO_MANY_REQUESTS_ERROR_CODE = 'PAY_TOO_MANY_REQUESTS_ERROR'; -export const PAY_INVALID_CHARGE_ERROR_CODE = 'PAY_INVALID_CHARGE_ERROR'; -export const PAY_INVALID_PARAMETER_ERROR_CODE = 'PAY_INVALID_PARAMETER_ERROR'; -export const UNCAUGHT_HYDRATE_CHARGE_ERROR_CODE = - 'UNCAUGHT_HYDRATE_CHARGE_ERROR'; +export const GENERAL_PAY_ERROR_MESSAGE = 'PAY_ERROR'; +export const PAY_UNSUPPORTED_CHAIN_ERROR_MESSAGE = 'UNSUPPORTED_CHAIN'; +export const PAY_TOO_MANY_REQUESTS_ERROR_MESSAGE = + 'PAY_TOO_MANY_REQUESTS_ERROR'; +export const PAY_INVALID_CHARGE_ERROR_MESSAGE = 'PAY_INVALID_CHARGE_ERROR'; +export const PAY_INVALID_PARAMETER_ERROR_MESSAGE = + 'PAY_INVALID_PARAMETER_ERROR'; +export const UNCAUGHT_PAY_ERROR_MESSAGE = 'UNCAUGHT_PAY_ERROR'; diff --git a/src/pay/types.ts b/src/pay/types.ts deleted file mode 100644 index 3f0648956..000000000 --- a/src/pay/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Address } from 'viem'; - -export type HydratedCharge = { - id: string; - callData: { - deadline: string; - feeAmount: string; - id: string; - operator: Address; - prefix: Address; - recipient: Address; - recipientAmount: string; - recipientCurrency: Address; - refundDestination: Address; - signature: Address; - }; - metaData: { - chainId: number; - contractAddress: Address; - sender: Address; - settlementCurrencyAddress: Address; - }; -}; diff --git a/src/pay/utils/getPayErrorCode.test.ts b/src/pay/utils/getPayErrorCode.test.ts deleted file mode 100644 index 48b08b33a..000000000 --- a/src/pay/utils/getPayErrorCode.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { - GENERAL_PAY_ERROR_CODE, - PAY_INVALID_CHARGE_ERROR_CODE, - PAY_INVALID_PARAMETER_ERROR_CODE, - PAY_TOO_MANY_REQUESTS_ERROR_CODE, -} from '../constants'; -import { getPayErrorCode } from './getPayErrorCode'; - -describe('getPayErrorCode', () => { - it('should return TOO_MANY_REQUESTS_ERROR_CODE for errorCode -32001', () => { - const result = getPayErrorCode(-32001); - expect(result).toBe(PAY_TOO_MANY_REQUESTS_ERROR_CODE); - }); - - it('should return PAY_INVALID_CHARGE_ERROR_CODE for errorCode -32601', () => { - const result = getPayErrorCode(-32601); - expect(result).toBe(PAY_INVALID_CHARGE_ERROR_CODE); - }); - - it('should return PAY_INVALID_PARAMETER_ERROR_CODE for errorCode -32602', () => { - const result = getPayErrorCode(-32602); - expect(result).toBe(PAY_INVALID_PARAMETER_ERROR_CODE); - }); - - it('should return GENERAL_PAY_ERROR_CODE for misc errorCode', () => { - const result = getPayErrorCode(-32603); - expect(result).toBe(GENERAL_PAY_ERROR_CODE); - }); - - it('should return GENERAL_PAY_ERROR_CODE for no errorCode', () => { - const result = getPayErrorCode(); - expect(result).toBe(GENERAL_PAY_ERROR_CODE); - }); -}); diff --git a/src/pay/utils/getPayErrorCode.ts b/src/pay/utils/getPayErrorCode.ts deleted file mode 100644 index f40558424..000000000 --- a/src/pay/utils/getPayErrorCode.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - GENERAL_PAY_ERROR_CODE, - PAY_INVALID_CHARGE_ERROR_CODE, - PAY_INVALID_PARAMETER_ERROR_CODE, - PAY_TOO_MANY_REQUESTS_ERROR_CODE, -} from '../constants'; - -export function getPayErrorCode(errorCode?: number) { - if (errorCode === -32001) { - return PAY_TOO_MANY_REQUESTS_ERROR_CODE; - } - - if (errorCode === -32601) { - return PAY_INVALID_CHARGE_ERROR_CODE; - } - - if (errorCode === -32602) { - return PAY_INVALID_PARAMETER_ERROR_CODE; - } - - return GENERAL_PAY_ERROR_CODE; -} diff --git a/src/pay/utils/getPayErrorMessage.test.ts b/src/pay/utils/getPayErrorMessage.test.ts new file mode 100644 index 000000000..82dd164aa --- /dev/null +++ b/src/pay/utils/getPayErrorMessage.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { + GENERAL_PAY_ERROR_MESSAGE, + PAY_INVALID_CHARGE_ERROR_MESSAGE, + PAY_INVALID_PARAMETER_ERROR_MESSAGE, + PAY_TOO_MANY_REQUESTS_ERROR_MESSAGE, + UNCAUGHT_PAY_ERROR_MESSAGE, +} from '../constants'; +import { getPayErrorMessage } from './getPayErrorMessage'; + +describe('getPayErrorMessage', () => { + it('should return TOO_MANY_REQUESTS_ERROR_MESSAGE for errorCode -32001', () => { + const result = getPayErrorMessage(-32001); + expect(result).toBe(PAY_TOO_MANY_REQUESTS_ERROR_MESSAGE); + }); + + it('should return PAY_INVALID_CHARGE_ERROR_MESSAGE for errorCode -32601', () => { + const result = getPayErrorMessage(-32601); + expect(result).toBe(PAY_INVALID_CHARGE_ERROR_MESSAGE); + }); + + it('should return PAY_INVALID_PARAMETER_ERROR_MESSAGE for errorCode -32602', () => { + const result = getPayErrorMessage(-32602); + expect(result).toBe(PAY_INVALID_PARAMETER_ERROR_MESSAGE); + }); + + it('should return GENERAL_PAY_ERROR_MESSAGE for misc errorCode', () => { + const result = getPayErrorMessage(-32603); + expect(result).toBe(GENERAL_PAY_ERROR_MESSAGE); + }); + + it('should return UNCAUGHT_PAY_ERROR_MESSAGE for no errorCode', () => { + const result = getPayErrorMessage(); + expect(result).toBe(UNCAUGHT_PAY_ERROR_MESSAGE); + }); +}); diff --git a/src/pay/utils/getPayErrorMessage.ts b/src/pay/utils/getPayErrorMessage.ts new file mode 100644 index 000000000..44f462c39 --- /dev/null +++ b/src/pay/utils/getPayErrorMessage.ts @@ -0,0 +1,27 @@ +import { + GENERAL_PAY_ERROR_MESSAGE, + PAY_INVALID_CHARGE_ERROR_MESSAGE, + PAY_INVALID_PARAMETER_ERROR_MESSAGE, + PAY_TOO_MANY_REQUESTS_ERROR_MESSAGE, + UNCAUGHT_PAY_ERROR_MESSAGE, +} from '../constants'; + +export function getPayErrorMessage(errorCode?: number) { + if (!errorCode) { + return UNCAUGHT_PAY_ERROR_MESSAGE; + } + + if (errorCode === -32001) { + return PAY_TOO_MANY_REQUESTS_ERROR_MESSAGE; + } + + if (errorCode === -32601) { + return PAY_INVALID_CHARGE_ERROR_MESSAGE; + } + + if (errorCode === -32602) { + return PAY_INVALID_PARAMETER_ERROR_MESSAGE; + } + + return GENERAL_PAY_ERROR_MESSAGE; +} From b7724c664be9179b75305aeb2b7df8c42cf7cc78 Mon Sep 17 00:00:00 2001 From: Andrew Reder Date: Fri, 30 Aug 2024 06:12:01 -0700 Subject: [PATCH 7/8] add type comments; renames --- src/api/buildPayTransaction.ts | 4 +-- src/api/bulidPayTransaction.test.ts | 8 +++--- src/api/types.ts | 38 +++++++++++++++-------------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/api/buildPayTransaction.ts b/src/api/buildPayTransaction.ts index 26853d713..2b8ee24f1 100644 --- a/src/api/buildPayTransaction.ts +++ b/src/api/buildPayTransaction.ts @@ -10,7 +10,7 @@ import type { } from './types'; export async function buildPayTransaction({ - payerAddress, + address, chainId, chargeId, }: BuildPayTransactionParams): Promise { @@ -27,7 +27,7 @@ export async function buildPayTransaction({ BuildPayTransactionResponse >(PAY_HYDRATE_CHARGE, [ { - sender: payerAddress, + sender: address, chainId: chainId, chargeId, }, diff --git a/src/api/bulidPayTransaction.test.ts b/src/api/bulidPayTransaction.test.ts index a13463f67..4a42d67ae 100644 --- a/src/api/bulidPayTransaction.test.ts +++ b/src/api/bulidPayTransaction.test.ts @@ -33,7 +33,7 @@ describe('buildPayTransaction', () => { it('should return a Pay Transaction', async () => { const mockParams: BuildPayTransactionParams = { - payerAddress: MOCK_VALID_PAYER_ADDRESS, + address: MOCK_VALID_PAYER_ADDRESS, chainId: base.id, chargeId: MOCK_VALID_CHARGE_ID, }; @@ -56,7 +56,7 @@ describe('buildPayTransaction', () => { it('should return an error for chains other than Base', async () => { const mockParams: BuildPayTransactionParams = { - payerAddress: MOCK_VALID_PAYER_ADDRESS, + address: MOCK_VALID_PAYER_ADDRESS, chainId: mainnet.id, chargeId: MOCK_VALID_CHARGE_ID, }; @@ -75,7 +75,7 @@ describe('buildPayTransaction', () => { it('should return an error if sendRequest fails', async () => { const mockParams: BuildPayTransactionParams = { - payerAddress: MOCK_VALID_PAYER_ADDRESS, + address: MOCK_VALID_PAYER_ADDRESS, chainId: base.id, chargeId: MOCK_VALID_CHARGE_ID, }; @@ -108,7 +108,7 @@ describe('buildPayTransaction', () => { it('should return an error object from buildPayTransaction', async () => { const mockParams: BuildPayTransactionParams = { - payerAddress: MOCK_VALID_PAYER_ADDRESS, + address: MOCK_VALID_PAYER_ADDRESS, chainId: base.id, chargeId: MOCK_INVALID_CHARGE_ID, }; diff --git a/src/api/types.ts b/src/api/types.ts index 1c079b7fb..14a5adf24 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -84,7 +84,7 @@ export type SwapAPIParams = GetQuoteAPIParams | GetSwapAPIParams; * Note: exported as public Type */ export type BuildPayTransactionParams = { - payerAddress: Address; // The address of the wallet paying + address: Address; // The address of the wallet paying chainId: number; // The Chain ID of the payment Network (only Base is supported) chargeId: string; // The ID of the Commerce Charge to be paid }; @@ -95,29 +95,31 @@ export type HydrateChargeAPIParams = { chargeId: string; // The ID of the Commerce Charge to be paid }; -export type HydratedCharge = { - id: string; +export type PayTransaction = { + id: string; // The id of the Commerce Charge to be paid callData: { - deadline: string; - feeAmount: string; - id: string; - operator: Address; - prefix: Address; - recipient: Address; - recipientAmount: string; - recipientCurrency: Address; - refundDestination: Address; - signature: Address; + // Collection of fields used to make the contract call to the Payment contract + deadline: string; // Timestamp of when the payment will expire and be unpayable + feeAmount: string; // The amount of the processing fee in the recipient currency + id: string; // The id of the prepared transaction + operator: Address; // The address of the operator of the Payment contract + prefix: Address; // The prefix of the signature generated by Commerce + recipient: Address; // The address funds will settle in + recipientAmount: string; // The amount the recipient will get in the recipient currency + recipientCurrency: Address; // The address of the currency being paid (always USDC) + refundDestination: Address; // The wallet address of the payer + signature: Address; // The signature generated by the Payment contract operator, encoding the payment details }; metaData: { - chainId: number; - contractAddress: Address; - sender: Address; - settlementCurrencyAddress: Address; + // Collection of metadata needed to make the contract call to the Payment Contract + chainId: number; // The chain this prepared transaction can be paid on + contractAddress: Address; // The address of the Payment contract + sender: Address; // The wallet address of the payer + settlementCurrencyAddress: Address; // The address of the currency being paid (always USDC) }; }; /** * Note: exported as public Type */ -export type BuildPayTransactionResponse = HydratedCharge | APIError; +export type BuildPayTransactionResponse = PayTransaction | APIError; From fd9f02b03f4687a2f5ff152a3b9f61c299162936 Mon Sep 17 00:00:00 2001 From: Andrew Reder Date: Sat, 31 Aug 2024 05:14:14 -0700 Subject: [PATCH 8/8] update error codes; expose buildPayTransaction; change file locations --- ...ransaction.test.ts => buildPayTransaction.test.ts} | 11 ++++------- src/api/buildPayTransaction.ts | 8 ++++---- src/api/index.ts | 3 +++ .../buildPayTransactionFixtures.ts => mocks.ts} | 0 src/{pay => api}/utils/getPayErrorMessage.test.ts | 2 +- src/{pay => api}/utils/getPayErrorMessage.ts | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) rename src/api/{bulidPayTransaction.test.ts => buildPayTransaction.test.ts} (97%) rename src/api/{fixtures/buildPayTransactionFixtures.ts => mocks.ts} (100%) rename src/{pay => api}/utils/getPayErrorMessage.test.ts (97%) rename src/{pay => api}/utils/getPayErrorMessage.ts (95%) diff --git a/src/api/bulidPayTransaction.test.ts b/src/api/buildPayTransaction.test.ts similarity index 97% rename from src/api/bulidPayTransaction.test.ts rename to src/api/buildPayTransaction.test.ts index 4a42d67ae..60e0b1486 100644 --- a/src/api/bulidPayTransaction.test.ts +++ b/src/api/buildPayTransaction.test.ts @@ -18,7 +18,7 @@ import { MOCK_INVALID_CHARGE_ID, MOCK_VALID_CHARGE_ID, MOCK_VALID_PAYER_ADDRESS, -} from './fixtures/buildPayTransactionFixtures'; +} from './mocks'; import type { BuildPayTransactionParams, HydrateChargeAPIParams, @@ -42,7 +42,6 @@ describe('buildPayTransaction', () => { chargeId: MOCK_VALID_CHARGE_ID, chainId: base.id, }; - (sendRequest as Mock).mockResolvedValue( MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE, ); @@ -60,13 +59,12 @@ describe('buildPayTransaction', () => { chainId: mainnet.id, chargeId: MOCK_VALID_CHARGE_ID, }; - (sendRequest as Mock).mockResolvedValue( MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE, ); const error = await buildPayTransaction(mockParams); expect(error).toEqual({ - code: 'AmBPT01', + code: 'AmBPTa01', error: 'Pay Transactions must be on Base', message: PAY_UNSUPPORTED_CHAIN_ERROR_MESSAGE, }); @@ -84,7 +82,6 @@ describe('buildPayTransaction', () => { chargeId: MOCK_VALID_CHARGE_ID, chainId: base.id, }; - (sendRequest as Mock).mockResolvedValue( MOCK_HYDRATE_CHARGE_SUCCESS_RESPONSE, ); @@ -100,7 +97,7 @@ describe('buildPayTransaction', () => { (sendRequest as Mock).mockRejectedValue(mockError); const error = await buildPayTransaction(mockParams); expect(error).toEqual({ - code: 'AmBPT03', + code: 'AmBPTa03', error: 'Something went wrong', message: UNCAUGHT_PAY_ERROR_MESSAGE, }); @@ -122,7 +119,7 @@ describe('buildPayTransaction', () => { ); const error = await buildPayTransaction(mockParams); expect(error).toEqual({ - code: 'AmBPT02', + code: 'AmBPTa02', error: 'method not found - Not found', message: PAY_INVALID_CHARGE_ERROR_MESSAGE, }); diff --git a/src/api/buildPayTransaction.ts b/src/api/buildPayTransaction.ts index 2b8ee24f1..8febe87f7 100644 --- a/src/api/buildPayTransaction.ts +++ b/src/api/buildPayTransaction.ts @@ -2,12 +2,12 @@ import { base } from 'viem/chains'; import { PAY_HYDRATE_CHARGE } from '../network/definitions/pay'; import { sendRequest } from '../network/request'; import { PAY_UNSUPPORTED_CHAIN_ERROR_MESSAGE } from '../pay/constants'; -import { getPayErrorMessage } from '../pay/utils/getPayErrorMessage'; import type { BuildPayTransactionParams, BuildPayTransactionResponse, HydrateChargeAPIParams, } from './types'; +import { getPayErrorMessage } from './utils/getPayErrorMessage'; export async function buildPayTransaction({ address, @@ -16,7 +16,7 @@ export async function buildPayTransaction({ }: BuildPayTransactionParams): Promise { if (chainId !== base.id) { return { - code: 'AmBPT01', // Api Module Build Pay Transaction Error 01 + code: 'AmBPTa01', // Api Module Build Pay Transaction Error 01 error: 'Pay Transactions must be on Base', message: PAY_UNSUPPORTED_CHAIN_ERROR_MESSAGE, }; @@ -34,7 +34,7 @@ export async function buildPayTransaction({ ]); if (res.error) { return { - code: 'AmBPT02', // Api Module Build Pay Transaction Error 02 + code: 'AmBPTa02', // Api Module Build Pay Transaction Error 02 error: res.error.message, message: getPayErrorMessage(res.error?.code), }; @@ -42,7 +42,7 @@ export async function buildPayTransaction({ return res.result; } catch (_error) { return { - code: 'AmBPT03', // Api Module Build Pay Transaction Error 03 + code: 'AmBPTa03', // Api Module Build Pay Transaction Error 03 error: 'Something went wrong', message: getPayErrorMessage(), }; diff --git a/src/api/index.ts b/src/api/index.ts index b53285221..0f88f9e12 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,9 +1,12 @@ // 🌲☀️🌲 export { buildSwapTransaction } from './buildSwapTransaction'; +export { buildPayTransaction } from './buildPayTransaction'; export { getSwapQuote } from './getSwapQuote'; export { getTokens } from './getTokens'; export type { APIError, + BuildPayTransactionParams, + BuildPayTransactionResponse, BuildSwapTransactionParams, GetSwapQuoteParams, GetSwapQuoteResponse, diff --git a/src/api/fixtures/buildPayTransactionFixtures.ts b/src/api/mocks.ts similarity index 100% rename from src/api/fixtures/buildPayTransactionFixtures.ts rename to src/api/mocks.ts diff --git a/src/pay/utils/getPayErrorMessage.test.ts b/src/api/utils/getPayErrorMessage.test.ts similarity index 97% rename from src/pay/utils/getPayErrorMessage.test.ts rename to src/api/utils/getPayErrorMessage.test.ts index 82dd164aa..1faaec0c4 100644 --- a/src/pay/utils/getPayErrorMessage.test.ts +++ b/src/api/utils/getPayErrorMessage.test.ts @@ -5,7 +5,7 @@ import { PAY_INVALID_PARAMETER_ERROR_MESSAGE, PAY_TOO_MANY_REQUESTS_ERROR_MESSAGE, UNCAUGHT_PAY_ERROR_MESSAGE, -} from '../constants'; +} from '../../pay/constants'; import { getPayErrorMessage } from './getPayErrorMessage'; describe('getPayErrorMessage', () => { diff --git a/src/pay/utils/getPayErrorMessage.ts b/src/api/utils/getPayErrorMessage.ts similarity index 95% rename from src/pay/utils/getPayErrorMessage.ts rename to src/api/utils/getPayErrorMessage.ts index 44f462c39..c269d8211 100644 --- a/src/pay/utils/getPayErrorMessage.ts +++ b/src/api/utils/getPayErrorMessage.ts @@ -4,7 +4,7 @@ import { PAY_INVALID_PARAMETER_ERROR_MESSAGE, PAY_TOO_MANY_REQUESTS_ERROR_MESSAGE, UNCAUGHT_PAY_ERROR_MESSAGE, -} from '../constants'; +} from '../../pay/constants'; export function getPayErrorMessage(errorCode?: number) { if (!errorCode) {