Skip to content

Commit

Permalink
feat: track nonce by chain (#219)
Browse files Browse the repository at this point in the history
* update repo interface

* track nonce separately by chain

* add unit tests
  • Loading branch information
ConjunctiveNormalForm authored Jun 13, 2023
1 parent f11ad29 commit e6fba69
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 14 deletions.
6 changes: 3 additions & 3 deletions lib/handlers/get-nonce/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ export class GetNonceHandler extends APIGLambdaHandler<
params: APIHandleRequestParams<ContainerInjected, RequestInjected, void, GetNonceQueryParams>
): Promise<ErrorResponse | Response<GetNonceResponse>> {
const {
requestInjected: { address, log },
requestInjected: { address, chainId, log },
containerInjected: { dbInterface },
} = params

try {
log.info({ address: address }, 'Getting nonce for address')
const nonce = await dbInterface.getNonceByAddress(address)
const nonce = await dbInterface.getNonceByAddressAndChain(address, chainId)
return {
statusCode: 200,
body: {
nonce: nonce,
},
}
} catch (e: unknown) {
log.error({ e }, 'Error getting nonce')
log.error({ e }, `Error getting nonce for address ${address} on chain ${chainId}`)
return {
statusCode: 500,
...(e instanceof Error && { errorCode: e.message }),
Expand Down
2 changes: 2 additions & 0 deletions lib/handlers/get-nonce/injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { GetNonceQueryParams } from './schema/index'

export interface RequestInjected extends ApiRInj {
address: string
chainId: number
}

export interface ContainerInjected {
Expand Down Expand Up @@ -41,6 +42,7 @@ export class GetNonceInjector extends ApiInjector<ContainerInjected, RequestInje
log,
requestId,
address: requestQueryParams.address,
chainId: requestQueryParams.chainId ?? 1,
}
}
}
2 changes: 2 additions & 0 deletions lib/handlers/get-nonce/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import FieldValidator from '../../../util/field-validator'

export const GetNonceQueryParamsJoi = Joi.object({
address: FieldValidator.isValidEthAddress().required(),
chainId: FieldValidator.isValidChainId(),
})

export type GetNonceQueryParams = {
address: string
chainId?: number
}

export type GetNonceResponse = {
Expand Down
2 changes: 1 addition & 1 deletion lib/repositories/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface BaseOrdersRepository {
getOrders: (limit: number, queryFilters: GetOrdersQueryParams, cursor?: string) => Promise<QueryResult>
getByOfferer: (offerer: string, limit: number) => Promise<QueryResult>
getByOrderStatus: (orderStatus: string, limit: number) => Promise<QueryResult>
getNonceByAddress: (address: string) => Promise<string>
getNonceByAddressAndChain: (address: string, chainId: number) => Promise<string>
updateOrderStatus: (
orderHash: string,
status: ORDER_STATUS,
Expand Down
6 changes: 3 additions & 3 deletions lib/repositories/orders-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ export class DynamoOrdersRepository implements BaseOrdersRepository {
return res.Item as OrderEntity
}

public async getNonceByAddress(address: string): Promise<string> {
const res = await this.nonceEntity.query(address, {
public async getNonceByAddressAndChain(address: string, chainId: number): Promise<string> {
const res = await this.nonceEntity.query(`${address}-${chainId}`, {
limit: 1,
reverse: true,
consistent: true,
Expand Down Expand Up @@ -220,7 +220,7 @@ export class DynamoOrdersRepository implements BaseOrdersRepository {
createdAt: currentTimestampInSeconds(),
}),
this.nonceEntity.updateTransaction({
offerer: order.offerer,
offerer: `${order.offerer}-${order.chainId}`,
nonce: order.nonce,
}),
],
Expand Down
11 changes: 7 additions & 4 deletions test/handlers/get-nonce.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ describe('Testing get nonce handler.', () => {

const requestInjectedMock = {
address: MOCK_ADDRESS,
chainId: 1,
log: { info: () => jest.fn(), error: () => jest.fn() },
}
const injectorPromiseMock: any = {
getContainerInjected: () => {
return {
dbInterface: {
getNonceByAddress: getNonceByAddressMock,
getNonceByAddressAndChain: getNonceByAddressMock,
},
}
},
Expand All @@ -25,6 +26,7 @@ describe('Testing get nonce handler.', () => {
const event = {
queryStringParameters: {
address: MOCK_ADDRESS,
chainId: 1,
},
body: null,
}
Expand All @@ -41,7 +43,7 @@ describe('Testing get nonce handler.', () => {

it('Testing valid request and response.', async () => {
const getNonceResponse = await getNonceHandler.handler(event as any, {} as any)
expect(getNonceByAddressMock).toBeCalledWith(requestInjectedMock.address)
expect(getNonceByAddressMock).toBeCalledWith(requestInjectedMock.address, 1)
expect(getNonceResponse).toMatchObject({
body: JSON.stringify({ nonce: MOCK_NONCE }),
statusCode: 200,
Expand All @@ -58,6 +60,7 @@ describe('Testing get nonce handler.', () => {
[{ address: '' }, '"address\\" is not allowed to be empty"'],
[{ address: '0xF53bDa7e0337BD456cDcDab0Ab24Db43E738065' }, 'VALIDATION ERROR: Invalid address'],
[{}, '"address\\" is required'],
[{ address: MOCK_ADDRESS, chainId: 'foo' }, '\\"chainId\\" must be one of [1, 137, 12341234]'],
])('Throws 400 with invalid query param %p', async (invalidQueryParam, bodyMsg) => {
const invalidEvent = {
...event,
Expand All @@ -77,7 +80,7 @@ describe('Testing get nonce handler.', () => {
async (invalidResponseField) => {
getNonceByAddressMock.mockReturnValue(invalidResponseField)
const getNonceResponse = await getNonceHandler.handler(event as any, {} as any)
expect(getNonceByAddressMock).toBeCalledWith(requestInjectedMock.address)
expect(getNonceByAddressMock).toBeCalledWith(requestInjectedMock.address, 1)
expect(getNonceResponse.statusCode).toEqual(500)
expect(getNonceResponse.body).toEqual(expect.stringContaining('INTERNAL_ERROR'))
}
Expand All @@ -89,7 +92,7 @@ describe('Testing get nonce handler.', () => {
throw error
})
const getNonceResponse = await getNonceHandler.handler(event as any, {} as any)
expect(getNonceByAddressMock).toBeCalledWith(requestInjectedMock.address)
expect(getNonceByAddressMock).toBeCalledWith(requestInjectedMock.address, 1)
expect(getNonceResponse).toMatchObject({
body: JSON.stringify({ errorCode: error.message }),
statusCode: 500,
Expand Down
22 changes: 19 additions & 3 deletions test/repositories/dynamo-repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ describe('OrdersRepository get nonce test', () => {
...MOCK_ORDER_1,
nonce: '4',
})
const nonce = await ordersRepository.getNonceByAddress('hayden.eth')
const nonce = await ordersRepository.getNonceByAddressAndChain('hayden.eth', MOCK_ORDER_1.chainId)
expect(nonce).toEqual('4')
})

Expand All @@ -453,16 +453,32 @@ describe('OrdersRepository get nonce test', () => {
orderHash: '0x4',
})
// at this point, there are three orders in the DB, two with nonce 2
const nonce = await ordersRepository.getNonceByAddress('hayden.eth')
const nonce = await ordersRepository.getNonceByAddressAndChain('hayden.eth', MOCK_ORDER_1.chainId)
expect(nonce).toEqual('2')
})

it('should generate random nonce for new address', async () => {
const spy = jest.spyOn(nonceUtil, 'generateRandomNonce')
const res = await ordersRepository.getNonceByAddress('random.eth')
const res = await ordersRepository.getNonceByAddressAndChain('random.eth')
expect(res).not.toBeUndefined()
expect(spy).toHaveBeenCalled()
})

it('should track nonce for the same address on different chains separately', async () => {
await ordersRepository.putOrderAndUpdateNonceTransaction({
...MOCK_ORDER_2,
nonce: '10',
})
await ordersRepository.putOrderAndUpdateNonceTransaction({
...MOCK_ORDER_2,
chainId: 1,
nonce: '20',
})
const nonce = await ordersRepository.getNonceByAddressAndChain(MOCK_ORDER_2.offerer, MOCK_ORDER_2.chainId)
expect(nonce).toEqual('10')
const nonce2 = await ordersRepository.getNonceByAddressAndChain(MOCK_ORDER_2.offerer, 1)
expect(nonce2).toEqual('20')
})
})

describe('OrdersRepository get order count by offerer test', () => {
Expand Down

0 comments on commit e6fba69

Please sign in to comment.