Skip to content

Commit 5757b7b

Browse files
fix: getCryptoApproveTransactionParams pricing from state (#6735)
## Explanation <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> Get pricing from state instead of fetching pricing from server in `getCryptoApproveTransactionParams` since that method would be called frequently by clients ## References <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs), highlighting breaking changes as necessary - [x] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Use cached `state.pricing` in `getCryptoApproveTransactionParams` (with new missing-pricing error) and add/register/export the `getBillingPortalUrl` messaging action; update tests and changelog. > > - **SubscriptionController**: > - Use `state.pricing` in `getCryptoApproveTransactionParams` instead of fetching; throw `Subscription pricing not found` if absent. > - Register new action handler `SubscriptionController:getBillingPortalUrl`. > - **Exports**: > - Export `SubscriptionControllerGetBillingPortalUrlAction`, `AllowedActions`, and `AllowedEvents` in `src/index.ts`. > - **Tests**: > - Update `getCryptoApproveTransactionParams` tests to seed `state.pricing` and add case for missing pricing. > - **Changelog**: > - Note change to read pricing from state in `getCryptoApproveTransactionParams`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b70ee72. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Chaitanya Potti <[email protected]>
1 parent 573939e commit 5757b7b

File tree

4 files changed

+194
-130
lines changed

4 files changed

+194
-130
lines changed

packages/subscription-controller/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Changed
1111

12+
- Get pricing from state instead of fetching pricing from server in `getCryptoApproveTransactionParams` ([#6735](https://github.com/MetaMask/core/pull/6735))
1213
- Bump `@metamask/utils` from `^11.8.0` to `^11.8.1` ([#6708](https://github.com/MetaMask/core/pull/6708))
1314

1415
## [0.4.0]

packages/subscription-controller/src/SubscriptionController.test.ts

Lines changed: 175 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -759,176 +759,223 @@ describe('SubscriptionController', () => {
759759

760760
describe('getCryptoApproveTransactionParams', () => {
761761
it('returns transaction params for crypto approve transaction', async () => {
762-
await withController(async ({ controller, mockService }) => {
763-
// Provide product pricing and crypto payment info with unitDecimals small to avoid integer div to 0
764-
mockService.getPricing.mockResolvedValue(MOCK_PRICE_INFO_RESPONSE);
765-
766-
const result = await controller.getCryptoApproveTransactionParams({
767-
chainId: '0x1',
768-
paymentTokenAddress: '0xtoken',
769-
productType: PRODUCT_TYPES.SHIELD,
770-
interval: RECURRING_INTERVALS.month,
771-
});
762+
await withController(
763+
{
764+
state: {
765+
pricing: MOCK_PRICE_INFO_RESPONSE,
766+
},
767+
},
768+
async ({ controller }) => {
769+
const result = await controller.getCryptoApproveTransactionParams({
770+
chainId: '0x1',
771+
paymentTokenAddress: '0xtoken',
772+
productType: PRODUCT_TYPES.SHIELD,
773+
interval: RECURRING_INTERVALS.month,
774+
});
772775

773-
expect(result).toStrictEqual({
774-
approveAmount: '9000000000000000000',
775-
paymentAddress: '0xspender',
776-
paymentTokenAddress: '0xtoken',
777-
chainId: '0x1',
778-
});
779-
});
776+
expect(result).toStrictEqual({
777+
approveAmount: '9000000000000000000',
778+
paymentAddress: '0xspender',
779+
paymentTokenAddress: '0xtoken',
780+
chainId: '0x1',
781+
});
782+
},
783+
);
780784
});
781785

782-
it('throws when product price not found', async () => {
783-
await withController(async ({ controller, mockService }) => {
784-
mockService.getPricing.mockResolvedValue({
785-
products: [],
786-
paymentMethods: [],
787-
});
788-
786+
it('throws when pricing not found', async () => {
787+
await withController(async ({ controller }) => {
789788
await expect(
790789
controller.getCryptoApproveTransactionParams({
791790
chainId: '0x1',
792791
paymentTokenAddress: '0xtoken',
793792
productType: PRODUCT_TYPES.SHIELD,
794793
interval: RECURRING_INTERVALS.month,
795794
}),
796-
).rejects.toThrow('Product price not found');
795+
).rejects.toThrow('Subscription pricing not found');
797796
});
798797
});
799798

799+
it('throws when product price not found', async () => {
800+
await withController(
801+
{
802+
state: {
803+
pricing: {
804+
products: [],
805+
paymentMethods: [],
806+
},
807+
},
808+
},
809+
async ({ controller }) => {
810+
await expect(
811+
controller.getCryptoApproveTransactionParams({
812+
chainId: '0x1',
813+
paymentTokenAddress: '0xtoken',
814+
productType: PRODUCT_TYPES.SHIELD,
815+
interval: RECURRING_INTERVALS.month,
816+
}),
817+
).rejects.toThrow('Product price not found');
818+
},
819+
);
820+
});
821+
800822
it('throws when price not found for interval', async () => {
801-
await withController(async ({ controller, mockService }) => {
802-
mockService.getPricing.mockResolvedValue({
803-
products: [
804-
{
805-
name: PRODUCT_TYPES.SHIELD,
806-
prices: [
823+
await withController(
824+
{
825+
state: {
826+
pricing: {
827+
products: [
807828
{
808-
interval: RECURRING_INTERVALS.year,
809-
currency: 'usd',
810-
unitAmount: 10,
811-
unitDecimals: 18,
812-
trialPeriodDays: 0,
813-
minBillingCycles: 1,
829+
name: PRODUCT_TYPES.SHIELD,
830+
prices: [
831+
{
832+
interval: RECURRING_INTERVALS.year,
833+
currency: 'usd',
834+
unitAmount: 10,
835+
unitDecimals: 18,
836+
trialPeriodDays: 0,
837+
minBillingCycles: 1,
838+
},
839+
],
814840
},
815841
],
842+
paymentMethods: [],
816843
},
817-
],
818-
paymentMethods: [],
819-
});
820-
821-
await expect(
822-
controller.getCryptoApproveTransactionParams({
823-
chainId: '0x1',
824-
paymentTokenAddress: '0xtoken',
825-
productType: PRODUCT_TYPES.SHIELD,
826-
interval: RECURRING_INTERVALS.month,
827-
}),
828-
).rejects.toThrow('Price not found');
829-
});
844+
},
845+
},
846+
async ({ controller }) => {
847+
await expect(
848+
controller.getCryptoApproveTransactionParams({
849+
chainId: '0x1',
850+
paymentTokenAddress: '0xtoken',
851+
productType: PRODUCT_TYPES.SHIELD,
852+
interval: RECURRING_INTERVALS.month,
853+
}),
854+
).rejects.toThrow('Price not found');
855+
},
856+
);
830857
});
831858

832859
it('throws when chains payment info not found', async () => {
833-
await withController(async ({ controller, mockService }) => {
834-
mockService.getPricing.mockResolvedValue({
835-
...MOCK_PRICE_INFO_RESPONSE,
836-
paymentMethods: [
837-
{
838-
type: PAYMENT_TYPES.byCard,
860+
await withController(
861+
{
862+
state: {
863+
pricing: {
864+
...MOCK_PRICE_INFO_RESPONSE,
865+
paymentMethods: [
866+
{
867+
type: PAYMENT_TYPES.byCard,
868+
},
869+
],
839870
},
840-
],
841-
});
842-
843-
await expect(
844-
controller.getCryptoApproveTransactionParams({
845-
chainId: '0x1',
846-
paymentTokenAddress: '0xtoken',
847-
productType: PRODUCT_TYPES.SHIELD,
848-
interval: RECURRING_INTERVALS.month,
849-
}),
850-
).rejects.toThrow('Chains payment info not found');
851-
});
871+
},
872+
},
873+
async ({ controller }) => {
874+
await expect(
875+
controller.getCryptoApproveTransactionParams({
876+
chainId: '0x1',
877+
paymentTokenAddress: '0xtoken',
878+
productType: PRODUCT_TYPES.SHIELD,
879+
interval: RECURRING_INTERVALS.month,
880+
}),
881+
).rejects.toThrow('Chains payment info not found');
882+
},
883+
);
852884
});
853885

854886
it('throws when invalid chain id', async () => {
855-
await withController(async ({ controller, mockService }) => {
856-
mockService.getPricing.mockResolvedValue({
857-
...MOCK_PRICE_INFO_RESPONSE,
858-
paymentMethods: [
859-
{
860-
type: PAYMENT_TYPES.byCrypto,
861-
chains: [
887+
await withController(
888+
{
889+
state: {
890+
pricing: {
891+
...MOCK_PRICE_INFO_RESPONSE,
892+
paymentMethods: [
862893
{
863-
chainId: '0x2',
864-
paymentAddress: '0xspender',
865-
tokens: [],
894+
type: PAYMENT_TYPES.byCrypto,
895+
chains: [
896+
{
897+
chainId: '0x2',
898+
paymentAddress: '0xspender',
899+
tokens: [],
900+
},
901+
],
866902
},
867903
],
868904
},
869-
],
870-
});
871-
872-
await expect(
873-
controller.getCryptoApproveTransactionParams({
874-
chainId: '0x1',
875-
paymentTokenAddress: '0xtoken',
876-
productType: PRODUCT_TYPES.SHIELD,
877-
interval: RECURRING_INTERVALS.month,
878-
}),
879-
).rejects.toThrow('Invalid chain id');
880-
});
905+
},
906+
},
907+
async ({ controller }) => {
908+
await expect(
909+
controller.getCryptoApproveTransactionParams({
910+
chainId: '0x1',
911+
paymentTokenAddress: '0xtoken',
912+
productType: PRODUCT_TYPES.SHIELD,
913+
interval: RECURRING_INTERVALS.month,
914+
}),
915+
).rejects.toThrow('Invalid chain id');
916+
},
917+
);
881918
});
882919

883920
it('throws when invalid token address', async () => {
884-
await withController(async ({ controller, mockService }) => {
885-
mockService.getPricing.mockResolvedValue(MOCK_PRICE_INFO_RESPONSE);
886-
887-
await expect(
888-
controller.getCryptoApproveTransactionParams({
889-
chainId: '0x1',
890-
paymentTokenAddress: '0xtoken-invalid',
891-
productType: PRODUCT_TYPES.SHIELD,
892-
interval: RECURRING_INTERVALS.month,
893-
}),
894-
).rejects.toThrow('Invalid token address');
895-
});
921+
await withController(
922+
{
923+
state: {
924+
pricing: MOCK_PRICE_INFO_RESPONSE,
925+
},
926+
},
927+
async ({ controller }) => {
928+
await expect(
929+
controller.getCryptoApproveTransactionParams({
930+
chainId: '0x1',
931+
paymentTokenAddress: '0xtoken-invalid',
932+
productType: PRODUCT_TYPES.SHIELD,
933+
interval: RECURRING_INTERVALS.month,
934+
}),
935+
).rejects.toThrow('Invalid token address');
936+
},
937+
);
896938
});
897939

898940
it('throws when conversion rate not found', async () => {
899-
await withController(async ({ controller, mockService }) => {
900-
// Valid product and chain/token, but token lacks conversion rate for currency
901-
mockService.getPricing.mockResolvedValue({
902-
...MOCK_PRICE_INFO_RESPONSE,
903-
paymentMethods: [
904-
{
905-
type: PAYMENT_TYPES.byCrypto,
906-
chains: [
941+
await withController(
942+
{
943+
state: {
944+
pricing: {
945+
...MOCK_PRICE_INFO_RESPONSE,
946+
paymentMethods: [
907947
{
908-
chainId: '0x1',
909-
paymentAddress: '0xspender',
910-
tokens: [
948+
type: PAYMENT_TYPES.byCrypto,
949+
chains: [
911950
{
912-
address: '0xtoken',
913-
decimals: 18,
914-
conversionRate: {},
951+
chainId: '0x1',
952+
paymentAddress: '0xspender',
953+
tokens: [
954+
{
955+
address: '0xtoken',
956+
decimals: 18,
957+
symbol: 'USDT',
958+
conversionRate: {} as { usd: string },
959+
},
960+
],
915961
},
916962
],
917963
},
918964
],
919965
},
920-
],
921-
});
922-
923-
await expect(
924-
controller.getCryptoApproveTransactionParams({
925-
chainId: '0x1',
926-
paymentTokenAddress: '0xtoken',
927-
productType: PRODUCT_TYPES.SHIELD,
928-
interval: RECURRING_INTERVALS.month,
929-
}),
930-
).rejects.toThrow('Conversion rate not found');
931-
});
966+
},
967+
},
968+
async ({ controller }) => {
969+
await expect(
970+
controller.getCryptoApproveTransactionParams({
971+
chainId: '0x1',
972+
paymentTokenAddress: '0xtoken',
973+
productType: PRODUCT_TYPES.SHIELD,
974+
interval: RECURRING_INTERVALS.month,
975+
}),
976+
).rejects.toThrow('Conversion rate not found');
977+
},
978+
);
932979
});
933980
});
934981

0 commit comments

Comments
 (0)