Skip to content

Commit

Permalink
feat: ID-1182 Add isRegisteredOffchain to IMXProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
haydenfowler authored Nov 6, 2023
1 parent a25e195 commit ef77fd5
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 2 deletions.
9 changes: 9 additions & 0 deletions packages/passport/sdk/src/starkEx/passportImxProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ export class PassportImxProvider implements IMXProvider {
);
}

// TODO: Remove once implemented
// eslint-disable-next-line class-methods-use-this
isRegisteredOffchain(): Promise<boolean> {
throw new PassportError(
'Operation not supported',
PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR,
);
}

// TODO: Remove once implemented
// eslint-disable-next-line class-methods-use-this
isRegisteredOnchain(): Promise<boolean> {
Expand Down
11 changes: 10 additions & 1 deletion packages/provider/src/genericImxProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Signers } from './signable-actions/types';
import { batchTransfer, transfer } from './signable-actions/transfer';
import { cancelOrder, createOrder } from './signable-actions/orders';
import {
isRegisteredOffchain,
isRegisteredOnChain,
registerOffchain,
} from './signable-actions/registration';
Expand All @@ -50,7 +51,15 @@ export class GenericIMXProvider implements IMXProvider {
}

async getAddress(): Promise<string> {
return await this.signers.ethSigner.getAddress();
return this.signers.ethSigner.getAddress();
}

async isRegisteredOffchain(): Promise<boolean> {
const ethAddress = await this.getAddress();
return isRegisteredOffchain(
ethAddress,
this.config,
);
}

registerOffchain(): Promise<RegisterUserResponse> {
Expand Down
6 changes: 6 additions & 0 deletions packages/provider/src/imxProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export interface IMXProvider {
* @return {Promise<RegisterUserResponse>} Returns a promise that resolves with the user registration response
*/
registerOffchain(): Promise<RegisterUserResponse>;
/**
* Checks if a User is registered off-chain
*
* @return {Promise<boolean>} Returns a promise that resolves with true if the User is registered with IMX, false otherwise
*/
isRegisteredOffchain(): Promise<boolean>;
/**
* Checks if a User is registered on-chain
*
Expand Down
64 changes: 63 additions & 1 deletion packages/provider/src/signable-actions/registration.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Contracts, UsersApi } from '@imtbl/core-sdk';
import { signRaw } from '@imtbl/toolkit';
import { AxiosError } from 'axios';
import { generateSigners, privateKey1, testConfig } from '../test/helpers';
import { isRegisteredOnChain, registerOffchain } from './registration';
import { isRegisteredOffchain, isRegisteredOnChain, registerOffchain } from './registration';

jest.mock('@imtbl/core-sdk');
jest.mock('@imtbl/toolkit');
Expand Down Expand Up @@ -39,6 +40,67 @@ describe('Registration', () => {
});
});

describe('isRegisteredOffchain', () => {
const getUsersMock = jest.fn();
const ethAddress = '0x123';

beforeEach(() => {
jest.restoreAllMocks();

(UsersApi as jest.Mock).mockReturnValue({
getUsers: getUsersMock,
});
});

describe('when the user has registered with IMX', () => {
test('should return true', async () => {
getUsersMock.mockResolvedValue({
data: {
accounts: [ethAddress],
},
});

const result = await isRegisteredOffchain(ethAddress, testConfig);

expect(result).toEqual(true);
expect(getUsersMock).toHaveBeenCalledTimes(1);
});
});

describe('when the user has not registered with IMX', () => {
test('should return false', async () => {
const axiosError = new AxiosError();
axiosError.response = {
config: axiosError.config!,
data: undefined,
headers: {},
request: undefined,
status: 404,
statusText: '',
};
getUsersMock.mockImplementation(() => Promise.reject(axiosError));

const result = await isRegisteredOffchain(ethAddress, testConfig);

expect(result).toEqual(false);
expect(getUsersMock).toHaveBeenCalledTimes(1);
});
});

describe('when getUsers throws an error that is not a 404', () => {
test('should throw the error', async () => {
const axiosResponse = new Error('oops');
getUsersMock.mockImplementation(() => Promise.reject(axiosResponse));

await expect(
isRegisteredOffchain(ethAddress, testConfig),
).rejects.toThrowError(axiosResponse);

expect(getUsersMock).toHaveBeenCalledTimes(1);
});
});
});

describe('registerOffchain', () => {
let getSignableRegistrationOffchainMock: jest.Mock;
let registerUserMock: jest.Mock;
Expand Down
18 changes: 18 additions & 0 deletions packages/provider/src/signable-actions/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
UsersApi,
} from '@imtbl/core-sdk';
import { signRaw } from '@imtbl/toolkit';
import { AxiosError } from 'axios';
import { Signers } from './types';
import { validateChain } from './helpers';
import { ProviderConfiguration } from '../config';
Expand Down Expand Up @@ -45,6 +46,23 @@ export async function registerOffchain(
return registeredUser.data;
}

export async function isRegisteredOffchain(ethAddress: string, config: ProviderConfiguration): Promise<boolean> {
try {
const usersApi = new UsersApi(config.immutableXConfig.apiConfiguration);
const getUsersResult = await usersApi.getUsers({
user: ethAddress,
});
const { accounts } = getUsersResult.data;

return accounts?.length > 0;
} catch (ex) {
if (ex instanceof AxiosError && ex.response?.status === 404) {
return false;
}
throw ex;
}
}

interface IsRegisteredCheckError {
reason: string;
}
Expand Down

0 comments on commit ef77fd5

Please sign in to comment.