-
Notifications
You must be signed in to change notification settings - Fork 158
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
feat: buildPayTransaction utilities in preparation for Pay button #1177
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
2013a8e
add hydrate charge utility
avidreder c268fb1
update error codes
avidreder aca460d
restructure hydration utility; update types
avidreder 0f2ef02
add tests; improve error handling
avidreder e7232f7
additional tests for error codes handling
avidreder 73b8986
rename to buildPayTransaction; update error handling with location codes
avidreder b7724c6
add type comments; renames
avidreder fd9f02b
update error codes; expose buildPayTransaction; change file locations
avidreder File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
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 './mocks'; | ||
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 = { | ||
address: 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 = { | ||
address: 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: 'AmBPTa01', | ||
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 = { | ||
address: 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: 'AmBPTa03', | ||
error: 'Something went wrong', | ||
message: UNCAUGHT_PAY_ERROR_MESSAGE, | ||
}); | ||
}); | ||
|
||
it('should return an error object from buildPayTransaction', async () => { | ||
const mockParams: BuildPayTransactionParams = { | ||
address: 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: 'AmBPTa02', | ||
error: 'method not found - Not found', | ||
message: PAY_INVALID_CHARGE_ERROR_MESSAGE, | ||
}); | ||
expect(sendRequest).toHaveBeenCalledTimes(1); | ||
expect(sendRequest).toHaveBeenCalledWith(PAY_HYDRATE_CHARGE, [ | ||
mockAPIParams, | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 type { | ||
BuildPayTransactionParams, | ||
BuildPayTransactionResponse, | ||
HydrateChargeAPIParams, | ||
} from './types'; | ||
import { getPayErrorMessage } from './utils/getPayErrorMessage'; | ||
|
||
export async function buildPayTransaction({ | ||
address, | ||
chainId, | ||
chargeId, | ||
}: BuildPayTransactionParams): Promise<BuildPayTransactionResponse> { | ||
if (chainId !== base.id) { | ||
return { | ||
code: 'AmBPTa01', // 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: address, | ||
chainId: chainId, | ||
chargeId, | ||
}, | ||
]); | ||
if (res.error) { | ||
return { | ||
code: 'AmBPTa02', // Api Module Build Pay Transaction Error 02 | ||
error: res.error.message, | ||
message: getPayErrorMessage(res.error?.code), | ||
}; | ||
} | ||
return res.result; | ||
} catch (_error) { | ||
return { | ||
code: 'AmBPTa03', // Api Module Build Pay Transaction Error 03 | ||
error: 'Something went wrong', | ||
message: getPayErrorMessage(), | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 '../../pay/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); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 '../../pay/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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const PAY_HYDRATE_CHARGE = 'pay_hydrateCharge'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const GENERAL_PAY_ERROR_MESSAGE = 'PAY_ERROR'; | ||
avidreder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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'; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where can I read more about what's a Commerce Charge? Do we have live docs anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably the page I would point to: https://docs.cdp.coinbase.com/commerce-onchain/docs/crypto-payments for an overview of what a charge is (though not great info for this context, we'll have to update it).
Where do you think we should add this link in the code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok let's come back on this one, mostly i am questioning the word
charge
.