Skip to content
Merged
15 changes: 15 additions & 0 deletions packages/claims-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Expose all public `ClaimsController` methods through its messenger ([#8219](https://github.com/MetaMask/core/pull/8219))
- The following actions are now available:
- `ClaimsController:fetchClaimsConfigurations`
- `ClaimsController:getSubmitClaimConfig`
- `ClaimsController:generateClaimSignature`
- `ClaimsController:getClaims`
- `ClaimsController:saveOrUpdateClaimDraft`
- `ClaimsController:getClaimDrafts`
- `ClaimsController:deleteClaimDraft`
- `ClaimsController:deleteAllClaimDrafts`
- `ClaimsController:clearState`
- Corresponding action types are now exported (e.g. `ClaimsControllerGetClaimsAction`)

### Changed

- Update dependencies ([#8236](https://github.com/MetaMask/core/pull/8236))
Expand Down
2 changes: 2 additions & 0 deletions packages/claims-controller/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"build:docs": "typedoc",
"changelog:update": "../../scripts/update-changelog.sh @metamask/claims-controller",
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/claims-controller",
"generate-method-action-types": "tsx ../../scripts/generate-method-action-types.ts",
"since-latest-release": "../../scripts/since-latest-release.sh",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
"test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
Expand All @@ -61,6 +62,7 @@
"deepmerge": "^4.2.2",
"jest": "^29.7.0",
"ts-jest": "^29.2.5",
"tsx": "^4.20.5",
"typedoc": "^0.25.13",
"typedoc-plugin-missing-exports": "^2.0.0",
"typescript": "~5.3.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* This file is auto generated by `scripts/generate-method-action-types.ts`.
* Do not edit manually.
*/

import type { ClaimsController } from './ClaimsController';

/**
* Fetch the required configurations for the claims service.
*
* @returns The required configurations for the claims service.
*/
export type ClaimsControllerFetchClaimsConfigurationsAction = {
type: `ClaimsController:fetchClaimsConfigurations`;
handler: ClaimsController['fetchClaimsConfigurations'];
};

/**
* Get required config for submitting a claim.
*
* @param claim - The claim request to get the required config for.
* @returns The required config for submitting the claim.
*/
export type ClaimsControllerGetSubmitClaimConfigAction = {
type: `ClaimsController:getSubmitClaimConfig`;
handler: ClaimsController['getSubmitClaimConfig'];
};

/**
* Generate a signature for a claim.
*
* @param chainId - The chain id of the claim.
* @param walletAddress - The impacted wallet address of the claim.
* @returns The signature for the claim.
*/
export type ClaimsControllerGenerateClaimSignatureAction = {
type: `ClaimsController:generateClaimSignature`;
handler: ClaimsController['generateClaimSignature'];
};

/**
* Get the list of claims for the current user.
*
* @returns The list of claims for the current user.
*/
export type ClaimsControllerGetClaimsAction = {
type: `ClaimsController:getClaims`;
handler: ClaimsController['getClaims'];
};

/**
* Save a claim draft to the state.
* If the draft name is not provided, a default name will be generated.
* If the draft with the same id already exists, it will be updated.
*
* @param draft - The draft to save.
* @returns The saved draft.
*/
export type ClaimsControllerSaveOrUpdateClaimDraftAction = {
type: `ClaimsController:saveOrUpdateClaimDraft`;
handler: ClaimsController['saveOrUpdateClaimDraft'];
};

/**
* Get the list of claim drafts.
*
* @returns The list of claim drafts.
*/
export type ClaimsControllerGetClaimDraftsAction = {
type: `ClaimsController:getClaimDrafts`;
handler: ClaimsController['getClaimDrafts'];
};

/**
* Delete a claim draft from the state.
*
* @param draftId - The ID of the draft to delete.
*/
export type ClaimsControllerDeleteClaimDraftAction = {
type: `ClaimsController:deleteClaimDraft`;
handler: ClaimsController['deleteClaimDraft'];
};

/**
* Delete all claim drafts from the state.
*/
export type ClaimsControllerDeleteAllClaimDraftsAction = {
type: `ClaimsController:deleteAllClaimDrafts`;
handler: ClaimsController['deleteAllClaimDrafts'];
};

/**
* Clears the claims state and resets to default values.
*/
export type ClaimsControllerClearStateAction = {
type: `ClaimsController:clearState`;
handler: ClaimsController['clearState'];
};

/**
* Union of all ClaimsController action types.
*/
export type ClaimsControllerMethodActions =
| ClaimsControllerFetchClaimsConfigurationsAction
| ClaimsControllerGetSubmitClaimConfigAction
| ClaimsControllerGenerateClaimSignatureAction
| ClaimsControllerGetClaimsAction
| ClaimsControllerSaveOrUpdateClaimDraftAction
| ClaimsControllerGetClaimDraftsAction
| ClaimsControllerDeleteClaimDraftAction
| ClaimsControllerDeleteAllClaimDraftsAction
| ClaimsControllerClearStateAction;
70 changes: 45 additions & 25 deletions packages/claims-controller/src/ClaimsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,11 @@ describe('ClaimsController', () => {
});

it('should fetch claims configurations successfully', async () => {
await withController(async ({ controller }) => {
await withController(async ({ controller, rootMessenger }) => {
const initialState = controller.state;
const configurations = await controller.fetchClaimsConfigurations();
const configurations = await rootMessenger.call(
'ClaimsController:fetchClaimsConfigurations',
);
expect(configurations).toBeDefined();

const expectedConfigurations = {
Expand Down Expand Up @@ -153,9 +155,11 @@ describe('ClaimsController', () => {
});

it('should be able to generate valid submit claim config', async () => {
await withController(async ({ controller }) => {
const submitClaimConfig =
await controller.getSubmitClaimConfig(MOCK_CLAIM);
await withController(async ({ rootMessenger }) => {
const submitClaimConfig = await rootMessenger.call(
'ClaimsController:getSubmitClaimConfig',
MOCK_CLAIM,
);

expect(mockClaimServiceRequestHeaders).toHaveBeenCalledTimes(1);
expect(mockClaimServiceGetClaimsApiUrl).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -183,9 +187,12 @@ describe('ClaimsController', () => {
],
},
},
async ({ controller }) => {
async ({ rootMessenger }) => {
await expect(
controller.getSubmitClaimConfig(MOCK_CLAIM),
rootMessenger.call(
'ClaimsController:getSubmitClaimConfig',
MOCK_CLAIM,
),
).rejects.toThrow(
ClaimsControllerErrorMessages.CLAIM_ALREADY_SUBMITTED,
);
Expand Down Expand Up @@ -213,8 +220,9 @@ describe('ClaimsController', () => {
});

it('should generate a message and signature successfully', async () => {
await withController(async ({ controller }) => {
const signature = await controller.generateClaimSignature(
await withController(async ({ rootMessenger }) => {
const signature = await rootMessenger.call(
'ClaimsController:generateClaimSignature',
1,
MOCK_WALLET_ADDRESS,
);
Expand All @@ -226,14 +234,18 @@ describe('ClaimsController', () => {
});

it('should throw an error if claims API response with invalid SIWE message', async () => {
await withController(async ({ controller }) => {
await withController(async ({ rootMessenger }) => {
mockClaimServiceGenerateMessageForClaimSignature.mockRestore();
mockClaimServiceGenerateMessageForClaimSignature.mockResolvedValueOnce({
message: 'invalid SIWE message',
nonce: 'B4Y8k8lGdMml0nrqk',
});
await expect(
controller.generateClaimSignature(1, MOCK_WALLET_ADDRESS),
rootMessenger.call(
'ClaimsController:generateClaimSignature',
1,
MOCK_WALLET_ADDRESS,
),
).rejects.toThrow(
ClaimsControllerErrorMessages.INVALID_SIGNATURE_MESSAGE,
);
Expand All @@ -243,12 +255,12 @@ describe('ClaimsController', () => {

describe('getClaims', () => {
it('should be able to get the list of claims', async () => {
await withController(async ({ controller }) => {
await withController(async ({ controller, rootMessenger }) => {
mockClaimsServiceGetClaims.mockResolvedValueOnce([
MOCK_CLAIM_1,
MOCK_CLAIM_2,
]);
const claims = await controller.getClaims();
const claims = await rootMessenger.call('ClaimsController:getClaims');
expect(claims).toBeDefined();
expect(claims).toStrictEqual([MOCK_CLAIM_1, MOCK_CLAIM_2]);
expect(mockClaimsServiceGetClaims).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -294,9 +306,12 @@ describe('ClaimsController', () => {
];

it('should be able to save a claim draft', async () => {
await withController(async ({ controller }) => {
await withController(async ({ controller, rootMessenger }) => {
const initialState = controller.state;
controller.saveOrUpdateClaimDraft(MOCK_DRAFT);
rootMessenger.call(
'ClaimsController:saveOrUpdateClaimDraft',
MOCK_DRAFT,
);
const updatedState = controller.state;
expect(updatedState).not.toBe(initialState);
expect(updatedState.drafts).toHaveLength(1);
Expand All @@ -316,8 +331,10 @@ describe('ClaimsController', () => {
drafts: MOCK_CLAIM_DRAFTS,
},
},
async ({ controller }) => {
const claimDrafts = controller.getClaimDrafts();
async ({ rootMessenger }) => {
const claimDrafts = rootMessenger.call(
'ClaimsController:getClaimDrafts',
);
expect(claimDrafts).toBeDefined();
expect(claimDrafts).toStrictEqual(MOCK_CLAIM_DRAFTS);
},
Expand All @@ -331,8 +348,8 @@ describe('ClaimsController', () => {
drafts: MOCK_CLAIM_DRAFTS,
},
},
async ({ controller }) => {
controller.saveOrUpdateClaimDraft({
async ({ controller, rootMessenger }) => {
rootMessenger.call('ClaimsController:saveOrUpdateClaimDraft', {
draftId: 'mock-draft-1',
chainId: '0x1',
email: 'test@test.com',
Expand All @@ -356,10 +373,13 @@ describe('ClaimsController', () => {
drafts: MOCK_CLAIM_DRAFTS,
},
},
async ({ controller }) => {
async ({ controller, rootMessenger }) => {
const initialState = controller.state;
expect(initialState.drafts).toHaveLength(2);
controller.deleteClaimDraft('mock-draft-1');
rootMessenger.call(
'ClaimsController:deleteClaimDraft',
'mock-draft-1',
);
const updatedState = controller.state;
expect(updatedState.drafts).toHaveLength(1);
expect(updatedState.drafts[0].draftId).toBe('mock-draft-2');
Expand All @@ -374,10 +394,10 @@ describe('ClaimsController', () => {
drafts: MOCK_CLAIM_DRAFTS,
},
},
async ({ controller }) => {
async ({ controller, rootMessenger }) => {
const initialState = controller.state;
expect(initialState.drafts).toHaveLength(2);
controller.deleteAllClaimDrafts();
rootMessenger.call('ClaimsController:deleteAllClaimDrafts');
const updatedState = controller.state;
expect(updatedState.drafts).toHaveLength(0);
},
Expand All @@ -397,11 +417,11 @@ describe('ClaimsController', () => {
})),
},
},
async ({ controller }) => {
async ({ controller, rootMessenger }) => {
expect(controller.state.claims).toHaveLength(2);
expect(controller.state.drafts).toHaveLength(2);

controller.clearState();
rootMessenger.call('ClaimsController:clearState');

expect(controller.state).toStrictEqual(
getDefaultClaimsControllerState(),
Expand Down
22 changes: 21 additions & 1 deletion packages/claims-controller/src/ClaimsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { KeyringControllerSignPersonalMessageAction } from '@metamask/keyri
import type { Messenger } from '@metamask/messenger';
import { bytesToHex, stringToBytes } from '@metamask/utils';

import type { ClaimsControllerMethodActions } from './ClaimsController-method-action-types';
import type {
ClaimsServiceFetchClaimsConfigurationsAction,
ClaimsServiceGenerateMessageForClaimSignatureAction,
Expand Down Expand Up @@ -37,7 +38,9 @@ export type ClaimsControllerGetStateAction = ControllerGetStateAction<
ClaimsControllerState
>;

export type ClaimsControllerActions = ClaimsControllerGetStateAction;
export type ClaimsControllerActions =
| ClaimsControllerGetStateAction
| ClaimsControllerMethodActions;

export type AllowedActions =
| ClaimsServiceFetchClaimsConfigurationsAction
Expand Down Expand Up @@ -99,6 +102,18 @@ export function getDefaultClaimsControllerState(): ClaimsControllerState {
};
}

const MESSENGER_EXPOSED_METHODS = [
'fetchClaimsConfigurations',
'getSubmitClaimConfig',
'generateClaimSignature',
'getClaims',
'saveOrUpdateClaimDraft',
'getClaimDrafts',
'deleteClaimDraft',
'deleteAllClaimDrafts',
'clearState',
] as const;

export class ClaimsController extends BaseController<
typeof CONTROLLER_NAME,
ClaimsControllerState,
Expand All @@ -111,6 +126,11 @@ export class ClaimsController extends BaseController<
name: CONTROLLER_NAME,
state: { ...getDefaultClaimsControllerState(), ...state },
});

this.messenger.registerMethodActionHandlers(
this,
MESSENGER_EXPOSED_METHODS,
);
}

/**
Expand Down
12 changes: 12 additions & 0 deletions packages/claims-controller/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ export type {
ClaimsControllerMessenger,
} from './ClaimsController';

export type {
ClaimsControllerFetchClaimsConfigurationsAction,
ClaimsControllerGetSubmitClaimConfigAction,
ClaimsControllerGenerateClaimSignatureAction,
ClaimsControllerGetClaimsAction,
ClaimsControllerSaveOrUpdateClaimDraftAction,
ClaimsControllerGetClaimDraftsAction,
ClaimsControllerDeleteClaimDraftAction,
ClaimsControllerDeleteAllClaimDraftsAction,
ClaimsControllerClearStateAction,
} from './ClaimsController-method-action-types';

export type {
Claim,
ClaimsControllerState,
Expand Down
Loading
Loading