Skip to content

Commit

Permalink
feat: ID-1428 Updated isRegisteredOffchain & getAddress to not wait f…
Browse files Browse the repository at this point in the history
…or signers
  • Loading branch information
haydenfowler authored Feb 23, 2024
1 parent fb9e8b0 commit dabd6f2
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ describe('PassportImxProvider', () => {
imxApiClients: new ImxApiClients({} as any),
});

await expect(pp.getAddress()).rejects.toThrow(new Error('error'));
await expect(pp.registerOffchain()).rejects.toThrow(new Error('error'));
});
});

Expand Down Expand Up @@ -354,6 +354,7 @@ describe('PassportImxProvider', () => {
['batchNftTransfer' as const, [] as NftTransferDetails[]],
['exchangeTransfer' as const, {} as UnsignedExchangeTransferRequest],
['getAddress' as const, {} as any],
['isRegisteredOffchain' as const, {} as any],
])('when the user has been logged out - %s', (methodName, args) => {
beforeEach(() => {
passportEventEmitter.emit(PassportEvents.LOGGED_OUT);
Expand All @@ -379,6 +380,7 @@ describe('PassportImxProvider', () => {
['batchNftTransfer' as const, [] as NftTransferDetails[]],
['exchangeTransfer' as const, {} as UnsignedExchangeTransferRequest],
['getAddress' as const, {} as any],
['isRegisteredOffchain' as const, {} as any],
])('when the user\'s access token is expired and cannot be retrieved', (methodName, args) => {
beforeEach(() => {
mockAuthManager.getUser.mockResolvedValue(null);
Expand Down
76 changes: 45 additions & 31 deletions packages/passport/sdk/src/starkEx/passportImxProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,6 @@ export interface PassportImxProviderOptions {
guardianClient: GuardianClient;
}

type AuthenticatedUserAndSigners = {
user: User;
starkSigner: StarkSigner;
ethSigner: EthSigner;
};

type RegisteredUserAndSigners = {
user: UserImx;
starkSigner: StarkSigner;
Expand All @@ -71,8 +65,8 @@ export class PassportImxProvider implements IMXProvider {
/**
* This property is set during initialisation and stores the signers in a promise.
* This property is not meant to be accessed directly, but through the
* `getAuthenticatedUserAndSigners` method.
* @see getAuthenticatedUserAndSigners
* `#getSigners` method.
* @see #getSigners
*/
private signers: Promise<IMXSigners | undefined> | undefined;

Expand All @@ -91,7 +85,7 @@ export class PassportImxProvider implements IMXProvider {
this.magicAdapter = magicAdapter;
this.imxApiClients = imxApiClients;
this.guardianClient = guardianClient;
this.initialiseSigners();
this.#initialiseSigners();

passportEventEmitter.on(PassportEvents.LOGGED_OUT, this.handleLogout);
}
Expand All @@ -109,10 +103,10 @@ export class PassportImxProvider implements IMXProvider {
* so that it doesn't result in an unhandled promise rejection.
*
* This error is thrown when the signers are requested through:
* @see getAuthenticatedUserAndSigners
* @see #getSigners
*
*/
private initialiseSigners() {
#initialiseSigners() {
const generateSigners = async (): Promise<IMXSigners> => {
const user = await this.authManager.getUser();
// The user will be present because the factory validates it
Expand All @@ -137,15 +131,20 @@ export class PassportImxProvider implements IMXProvider {
});
}

protected async getAuthenticatedUserAndSigners(): Promise<AuthenticatedUserAndSigners> {
async #getAuthenticatedUser(): Promise<User> {
const user = await this.authManager.getUser();

if (!user || !this.signers) {
throw new PassportError(
'User has been logged out',
PassportErrorType.NOT_LOGGED_IN_ERROR,
);
}

return user;
}

async #getSigners(): Promise<IMXSigners> {
const signers = await this.signers;
// Throw the stored error if the signers failed to initialise
if (typeof signers === 'undefined') {
Expand All @@ -155,11 +154,14 @@ export class PassportImxProvider implements IMXProvider {
throw new Error('Signers failed to initialise');
}

return { user, ...signers };
return signers;
}

protected async getRegisteredImxUserAndSigners(): Promise<RegisteredUserAndSigners> {
const { user, starkSigner, ethSigner } = await this.getAuthenticatedUserAndSigners();
async #getRegisteredImxUserAndSigners(): Promise<RegisteredUserAndSigners> {
const [user, signers] = await Promise.all([
this.#getAuthenticatedUser(),
this.#getSigners(),
]);

if (!isUserImx(user)) {
throw new PassportError(
Expand All @@ -168,11 +170,15 @@ export class PassportImxProvider implements IMXProvider {
);
}

return { user, starkSigner, ethSigner };
return {
user,
starkSigner: signers.starkSigner,
ethSigner: signers.ethSigner,
};
}

async transfer(request: UnsignedTransferRequest): Promise<CreateTransferResponseV1> {
const { user, starkSigner } = await this.getRegisteredImxUserAndSigners();
const { user, starkSigner } = await this.#getRegisteredImxUserAndSigners();

return transfer({
request,
Expand All @@ -184,22 +190,26 @@ export class PassportImxProvider implements IMXProvider {
}

async registerOffchain(): Promise<RegisterUserResponse> {
const { user, ethSigner, starkSigner } = await this.getAuthenticatedUserAndSigners();
const [user, signers] = await Promise.all([
this.#getAuthenticatedUser(),
this.#getSigners(),
]);

return await registerOffchain(
ethSigner,
starkSigner,
signers.ethSigner,
signers.starkSigner,
user,
this.authManager,
this.imxApiClients,
);
}

async isRegisteredOffchain(): Promise<boolean> {
const { user } = await this.getAuthenticatedUserAndSigners();
const user = await this.#getAuthenticatedUser();

return !!user.imx;
}

// TODO: Remove once implemented
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
isRegisteredOnchain(): Promise<boolean> {
throw new PassportError(
Expand All @@ -209,7 +219,7 @@ export class PassportImxProvider implements IMXProvider {
}

async createOrder(request: UnsignedOrderRequest): Promise<CreateOrderResponse> {
const { user, starkSigner } = await this.getRegisteredImxUserAndSigners();
const { user, starkSigner } = await this.#getRegisteredImxUserAndSigners();

return createOrder({
request,
Expand All @@ -223,7 +233,7 @@ export class PassportImxProvider implements IMXProvider {
async cancelOrder(
request: GetSignableCancelOrderRequest,
): Promise<CancelOrderResponse> {
const { user, starkSigner } = await this.getRegisteredImxUserAndSigners();
const { user, starkSigner } = await this.#getRegisteredImxUserAndSigners();

return cancelOrder({
request,
Expand All @@ -235,7 +245,7 @@ export class PassportImxProvider implements IMXProvider {
}

async createTrade(request: GetSignableTradeRequest): Promise<CreateTradeResponse> {
const { user, starkSigner } = await this.getRegisteredImxUserAndSigners();
const { user, starkSigner } = await this.#getRegisteredImxUserAndSigners();

return createTrade({
request,
Expand All @@ -249,7 +259,7 @@ export class PassportImxProvider implements IMXProvider {
async batchNftTransfer(
request: NftTransferDetails[],
): Promise<CreateTransferResponse> {
const { user, starkSigner } = await this.getRegisteredImxUserAndSigners();
const { user, starkSigner } = await this.#getRegisteredImxUserAndSigners();

return batchNftTransfer({
request,
Expand All @@ -263,7 +273,7 @@ export class PassportImxProvider implements IMXProvider {
async exchangeTransfer(
request: UnsignedExchangeTransferRequest,
): Promise<CreateTransferResponseV1> {
const { user, starkSigner } = await this.getRegisteredImxUserAndSigners();
const { user, starkSigner } = await this.#getRegisteredImxUserAndSigners();

return exchangeTransfer({
request,
Expand All @@ -273,7 +283,6 @@ export class PassportImxProvider implements IMXProvider {
});
}

// TODO: Remove once implemented
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
deposit(deposit: TokenAmount): Promise<TransactionResponse> {
throw new PassportError(
Expand All @@ -282,7 +291,6 @@ export class PassportImxProvider implements IMXProvider {
);
}

// TODO: Remove once implemented
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
prepareWithdrawal(request: TokenAmount): Promise<CreateWithdrawalResponse> {
throw new PassportError(
Expand All @@ -291,7 +299,6 @@ export class PassportImxProvider implements IMXProvider {
);
}

// TODO: Remove once implemented
// eslint-disable-next-line class-methods-use-this
completeWithdrawal(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand All @@ -306,7 +313,14 @@ export class PassportImxProvider implements IMXProvider {
}

async getAddress(): Promise<string> {
const { user } = await this.getRegisteredImxUserAndSigners();
const user = await this.#getAuthenticatedUser();
if (!isUserImx(user)) {
throw new PassportError(
'User has not been registered with StarkEx',
PassportErrorType.USER_NOT_REGISTERED_ERROR,
);
}

return Promise.resolve(user.imx.ethAddress);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ describe('registerOffchain', () => {
const imxApiClients = new ImxApiClients({} as any);
// create axios error with status 409
const err = new AxiosError('User already registered');
err.status = 409;
err.response = {
...err.response,
status: 409,
} as typeof err.response;

(registerPassportStarkEx as jest.Mock).mockRejectedValue(err);
mockForceUserRefresh.mockResolvedValue(mockUserImx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default async function registerOffchain(

return response;
} catch (err: any) {
if (axios.isAxiosError(err) && err.status === 409) {
if (axios.isAxiosError(err) && err.response?.status === 409) {
// The user already registered, but the user token is not updated yet.
await forceUserRefresh(authManager);
return { tx_hash: '' };
Expand Down

0 comments on commit dabd6f2

Please sign in to comment.