Skip to content

Commit

Permalink
Add /v2/owners/:ownerAddress/safes to gracefully handle errors (#2266)
Browse files Browse the repository at this point in the history
Adds a new endpoint (`/v2/owners/:ownerAddress/safes`) which returns `null` for every chain which the Transaction Service threw for:

- Add new scaffolding for v2 endpoint, deprecating previous.
- Wrap Transaction Service requests in `Promise.allSettled`.
- Construct all Safe lists from successful requests, logging for errorneous ones.
- Add appropriate test coverage.
  • Loading branch information
iamacook authored Jan 22, 2025
1 parent 69e5980 commit b89345f
Show file tree
Hide file tree
Showing 8 changed files with 495 additions and 7 deletions.
6 changes: 5 additions & 1 deletion src/domain/safe/safe.repository.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,14 @@ export interface ISafeRepository {
ownerAddress: `0x${string}`;
}): Promise<SafeList>;

getAllSafesByOwner(args: {
deprecated__getAllSafesByOwner(args: {
ownerAddress: `0x${string}`;
}): Promise<{ [chainId: string]: Array<string> }>;

getAllSafesByOwner(args: {
ownerAddress: `0x${string}`;
}): Promise<{ [chainId: string]: Array<string> | null }>;

getLastTransactionSortedByNonce(args: {
chainId: string;
safeAddress: `0x${string}`;
Expand Down
40 changes: 39 additions & 1 deletion src/domain/safe/safe.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,9 @@ export class SafeRepository implements ISafeRepository {
return SafeListSchema.parse(safeList);
}

async getAllSafesByOwner(args: {
// TODO: Remove with /owners/:ownerAddress/safes
// @deprecated
async deprecated__getAllSafesByOwner(args: {
ownerAddress: `0x${string}`;
}): Promise<{ [chainId: string]: Array<string> }> {
const chains = await this.chainsRepository.getAllChains();
Expand All @@ -449,6 +451,42 @@ export class SafeRepository implements ISafeRepository {
}, {});
}

async getAllSafesByOwner(args: {
ownerAddress: `0x${string}`;
}): Promise<{ [chainId: string]: Array<string> | null }> {
const chains = await this.chainsRepository.getAllChains();
const allSafeLists = await Promise.allSettled(
chains.map(async ({ chainId }) => {
const safeList = await this.getSafesByOwner({
chainId,
ownerAddress: args.ownerAddress,
});

return {
chainId,
safeList,
};
}),
);

const result: { [chainId: string]: Array<string> | null } = {};

for (const [index, allSafeList] of allSafeLists.entries()) {
const chainId = chains[index].chainId;

if (allSafeList.status === 'fulfilled') {
result[chainId] = allSafeList.value.safeList.safes;
} else {
result[chainId] = null;
this.loggingService.warn(
`Failed to fetch Safe owners. chainId=${chainId}`,
);
}
}

return result;
}

async getLastTransactionSortedByNonce(args: {
chainId: string;
safeAddress: `0x${string}`;
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AddressSchema } from '@/validation/entities/schemas/address.schema';
path: '',
version: '1',
})
export class OwnersController {
export class OwnersControllerV1 {
constructor(private readonly ownersService: OwnersService) {}

@ApiOkResponse({ type: SafeList })
Expand All @@ -29,6 +29,6 @@ export class OwnersController {
@Param('ownerAddress', new ValidationPipe(AddressSchema))
ownerAddress: `0x${string}`,
): Promise<{ [chainId: string]: Array<string> }> {
return this.ownersService.getAllSafesByOwner({ ownerAddress });
return this.ownersService.deprecated__getAllSafesByOwner({ ownerAddress });
}
}
Loading

0 comments on commit b89345f

Please sign in to comment.