From 6c892f6cbd57f3cd322bdbf94c962e62aa14dc1b Mon Sep 17 00:00:00 2001 From: just-a-node Date: Fri, 17 May 2024 01:09:16 -0600 Subject: [PATCH 1/3] fix: parser validation explicitly checks for undefined and null fields --- .../subgraph/src/lib/helpers/parse.ts | 48 +++++++++---------- .../subgraph/test/lib/helpers/parse.spec.ts | 26 ++++++++++ 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/packages/adapters/subgraph/src/lib/helpers/parse.ts b/packages/adapters/subgraph/src/lib/helpers/parse.ts index ced33079bb..e21bd8de15 100644 --- a/packages/adapters/subgraph/src/lib/helpers/parse.ts +++ b/packages/adapters/subgraph/src/lib/helpers/parse.ts @@ -66,7 +66,7 @@ export const originTransfer = (entity: any, asset: Record): Ori "to", "callData", ]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `OriginTransfer` entity parser: Transfer entity missing required field", { missingField: field, entity, @@ -174,7 +174,7 @@ export const destinationTransfer = (entity: any): DestinationTransfer => { "status", "routers", ]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `DestinationTransfer` entity parser: Transfer entity missing required field", { missingField: field, entity, @@ -271,7 +271,7 @@ export const originMessage = (entity: any): OriginMessage => { throw new NxtpError("Subgraph `OriginMessage` entity parser: OriginMessage entity is `undefined`."); } for (const field of ["index", "leaf", "root", "domain", "destinationDomain", "transferId", "message"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `OriginMessage` entity parser: Message entity missing required field", { missingField: field, entity, @@ -324,7 +324,7 @@ export const rootMessage = (entity: any): RootMessage => { throw new NxtpError("Subgraph `RootMessage` entity parser: RootMessage, entity is `undefined`."); } for (const field of ["id", "spokeDomain", "hubDomain", "root", "timestamp", "gasPrice", "gasLimit", "blockNumber"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `RootMessage` entity parser: Message entity missing required field", { missingField: field, entity, @@ -355,7 +355,7 @@ export const aggregatedRoot = (entity: any): AggregatedRoot => { throw new NxtpError("Subgraph `AggregatedRoot` entity parser: AggregatedRoot, entity is `undefined`."); } for (const field of ["id", "domain", "receivedRoot", "index"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `AggregatedRoot` entity parser: Message entity missing required field", { missingField: field, entity, @@ -377,7 +377,7 @@ export const propagatedRoot = (entity: any): PropagatedRoot => { throw new NxtpError("Subgraph `PropagatedRoot` entity parser: PropagatedRoot, entity is `undefined`."); } for (const field of ["id", "aggregate", "domainsHash", "count"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `PropagatedRoot` entity parser: Message entity missing required field", { missingField: field, entity, @@ -407,7 +407,7 @@ export const proposedRoot = (entity: any): Snapshot => { "baseAggregateRoot", "timestamp", ]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `proposedRoot` entity parser: Message entity missing required field", { missingField: field, entity, @@ -431,7 +431,7 @@ export const proposedSpokeOptimisticRoot = (entity: any): SpokeOptimisticRoot => throw new NxtpError("Subgraph `SpokeOptimisticRoot` entity parser: SpokeOptimisticRoot, entity is `undefined`."); } for (const field of ["id", "aggregateRoot", "rootTimestamp", "endOfDispute", "domain", "timestamp"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `SpokeOptimisticRoot` entity parser: Message entity missing required field", { missingField: field, entity, @@ -454,7 +454,7 @@ export const snapshotRoot = (entity: any): SnapshotRoot => { throw new NxtpError("Subgraph `snapshotRoot` entity parser: snapshotRoot, entity is `undefined`."); } for (const field of ["id", "spokeDomain", "root", "count", "timestamp", "blockNumber"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `snapshotRoot` entity parser: Message entity missing required field", { missingField: field, entity, @@ -478,7 +478,7 @@ export const finalizedRoot = (entity: any): OptimisticRootFinalized => { throw new NxtpError("Subgraph `finalizedRoot` entity parser: finalizedRoot, entity is `undefined`."); } for (const field of ["id", "aggregateRoot", "timestamp"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `finalizedRoot` entity parser: Message entity missing required field", { missingField: field, entity, @@ -501,7 +501,7 @@ export const propagatedOptimisticRoot = (entity: any): OptimisticRootPropagated ); } for (const field of ["id", "aggregateRoot", "domainsHash", "timestamp"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `propagatedOptimisticRoot` entity parser: Message entity missing required field", { missingField: field, entity, @@ -523,7 +523,7 @@ export const connectorMeta = (entity: any): ConnectorMeta => { throw new NxtpError("Subgraph `ConnectorMeta` entity parser: ConnectorMeta, entity is `undefined`."); } for (const field of ["id", "spokeDomain", "hubDomain", "rootManager", "mirrorConnector", "amb"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `ConnectorMeta` entity parser: Message entity missing required field", { missingField: field, entity, @@ -547,7 +547,7 @@ export const rootManagerMeta = (entity: any): RootManagerMeta => { throw new NxtpError("Subgraph `RootManagerMeta` entity parser: RootManagerMeta, entity is `undefined`."); } for (const field of ["id", "connectors", "domains"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `RootManagerMeta` entity parser: Message entity missing required field", { missingField: field, entity, @@ -568,7 +568,7 @@ export const rootManagerMode = (entity: any): RootManagerMode => { throw new NxtpError("Subgraph `RootManagerMode` entity parser: RootManagerMode, entity is `undefined`."); } for (const field of ["id", "mode"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `RootManagerMode` entity parser: Message entity missing required field", { missingField: field, entity, @@ -588,7 +588,7 @@ export const spokeConnectorMode = (entity: any): SpokeConnectorMode => { throw new NxtpError("Subgraph `SpokeConnectorMode` entity parser: SpokeConnectorMode, entity is `undefined`."); } for (const field of ["id", "mode"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `SpokeConnectorMode` entity parser: Message entity missing required field", { missingField: field, entity, @@ -610,7 +610,7 @@ export const receivedAggregateRoot = (entity: any): ReceivedAggregateRoot => { ); } for (const field of ["id", "root", "domain", "blockNumber"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `ReceivedAggregateRoot` entity parser: Message entity missing required field", { missingField: field, entity, @@ -646,7 +646,7 @@ export const stableSwapPool = (entity: any): StableSwapPool => { "invariant", "lpTokenSupply", ]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `StableSwapPool` entity parser: Message entity missing required field", { missingField: field, entity, @@ -696,7 +696,7 @@ export const stableSwapExchange = (entity: any): StableSwapExchange => { "timestamp", "nonce", ]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `stableSwapExchange` entity parser: Message entity missing required field", { missingField: field, entity, @@ -751,7 +751,7 @@ export const stableSwapPoolEvent = (entity: any): StableSwapPoolEvent => { "timestamp", "nonce", ]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `stableSwapPoolEvent` entity parser: Message entity missing required field", { missingField: field, entity, @@ -808,7 +808,7 @@ export const stableSwapLpTransfer = (entity: any): StableSwapTransfer => { "timestamp", "nonce", ]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `stableSwapLpTransfer` entity parser: Message entity missing required field", { missingField: field, entity, @@ -841,7 +841,7 @@ export const relayerFeesIncrease = (entity: any): RelayerFeesIncrease => { throw new NxtpError("Subgraph `RelayerFeesIncrease` entity parser: RelayerFeesIncrease, entity is `undefined`."); } for (const field of ["id", "increase"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `RelayerFeesIncrease` entity parser: Message entity missing required field", { missingField: field, entity, @@ -865,7 +865,7 @@ export const slippageUpdate = (entity: any): SlippageUpdate => { throw new NxtpError("Subgraph `SlippageUpdate` entity parser: SlippageUpdate, entity is `undefined`."); } for (const field of ["id", "slippage"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `SlippageUpdate` entity parser: Message entity missing required field", { missingField: field, entity, @@ -888,7 +888,7 @@ export const routerDailyTvl = (entity: any): RouterDailyTVL => { throw new NxtpError("Subgraph `RouterDailyTVL` entity parser: RouterDailyTVL, entity is `undefined`."); } for (const field of ["id", "asset", "router", "timestamp", "balance"]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `RouterDailyTVL` entity parser: Message entity missing required field", { missingField: field, entity, @@ -926,7 +926,7 @@ export const routerLiquidityEvent = (entity: any): RouterLiquidityEvent => { "timestamp", "nonce", ]) { - if (!entity[field]) { + if (entity[field] === undefined || entity[field] === null) { throw new NxtpError("Subgraph `routerLiquidityEvent` entity parser: Message entity missing required field", { missingField: field, entity, diff --git a/packages/adapters/subgraph/test/lib/helpers/parse.spec.ts b/packages/adapters/subgraph/test/lib/helpers/parse.spec.ts index b86940a1d4..17c4c46ae6 100644 --- a/packages/adapters/subgraph/test/lib/helpers/parse.spec.ts +++ b/packages/adapters/subgraph/test/lib/helpers/parse.spec.ts @@ -554,6 +554,32 @@ describe("Helpers:parse", () => { }).to.throw("Subgraph `StableSwapPool` entity parser: Message entity missing required field"); }); + it("should not throw if a required boolean field is false", () => { + const entity = { + key: mkBytes32("0xa"), + domain: "1111", + isActive: false, + lpToken: mkAddress("0xa"), + initialA: 200, + futureA: 200, + initialATime: 0, + futureATime: 0, + swapFee: "400000", + adminFee: "0", + pooledTokens: [mkAddress("0xa"), mkAddress("0xb")], + tokenPrecisionMultipliers: ["1", "1"], + poolTokenDecimals: [18, 18], + balances: ["200000", "200000"], + virtualPrice: "400000", + invariant: "0", + lpTokenSupply: "0", + }; + + expect(() => { + stableSwapPool(entity); + }).not.to.throw("Subgraph `StableSwapPool` entity parser: Message entity missing required field"); + }); + it("should parse valid swap pool", () => { const entity = { key: mkBytes32("0xa"), From be16d7d0e7ea811fcfb49e24ca514f6cc7e84647 Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Fri, 17 May 2024 11:18:58 -0300 Subject: [PATCH 2/3] feat: improve txservice logs --- packages/adapters/txservice/src/aggregator.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/adapters/txservice/src/aggregator.ts b/packages/adapters/txservice/src/aggregator.ts index 61edf12675..29d5027fac 100644 --- a/packages/adapters/txservice/src/aggregator.ts +++ b/packages/adapters/txservice/src/aggregator.ts @@ -295,7 +295,21 @@ export class RpcProviderAggregator { return await provider.call(formatted, blockTag); } } catch (error: unknown) { - throw new TransactionReadError(TransactionReadError.reasons.ContractReadError, { error }); + throw new TransactionReadError(TransactionReadError.reasons.ContractReadError, { + error, + provider: { + url: provider.name, + blockNumber: provider.syncedBlockNumber, + lag: provider.lag, + synced: provider.synced, + metrics: { + reliability: provider.reliability, + latency: provider.latency, + cps: provider.cps, + priority: provider.priority, + }, + }, + }); } }); } From bb56191a99ab1d7bd170058e7f4735a4a79cf55d Mon Sep 17 00:00:00 2001 From: 0xkenj1 Date: Fri, 17 May 2024 16:42:48 -0300 Subject: [PATCH 3/3] fix: unit tests --- packages/adapters/txservice/src/aggregator.ts | 2 -- .../txservice/test/aggregator.spec.ts | 23 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/adapters/txservice/src/aggregator.ts b/packages/adapters/txservice/src/aggregator.ts index 29d5027fac..fbaca6cde6 100644 --- a/packages/adapters/txservice/src/aggregator.ts +++ b/packages/adapters/txservice/src/aggregator.ts @@ -304,8 +304,6 @@ export class RpcProviderAggregator { synced: provider.synced, metrics: { reliability: provider.reliability, - latency: provider.latency, - cps: provider.cps, priority: provider.priority, }, }, diff --git a/packages/adapters/txservice/test/aggregator.spec.ts b/packages/adapters/txservice/test/aggregator.spec.ts index d70cf3cfd6..33e1adc0f3 100644 --- a/packages/adapters/txservice/test/aggregator.spec.ts +++ b/packages/adapters/txservice/test/aggregator.spec.ts @@ -82,6 +82,10 @@ describe("RpcProviderAggregator", () => { coreSyncProvider = Sinon.createStubInstance(SyncProvider); (coreSyncProvider as any)._syncedBlockNumber = 123; (coreSyncProvider as any).synced = true; + (coreSyncProvider as any).name = "-------fakeProvider------"; + (coreSyncProvider as any).lag = 1; + (coreSyncProvider as any).reliability = 1.0; + (coreSyncProvider as any).priority = 0; (coreSyncProvider as any).url = "https://-------fakeProvider------"; Sinon.stub(coreSyncProvider, "syncedBlockNumber").get(() => (coreSyncProvider as any)._syncedBlockNumber); (chainProvider as any).providers = [coreSyncProvider]; @@ -290,9 +294,24 @@ describe("RpcProviderAggregator", () => { it("should return error result if the signer readContract call throws", async () => { const testError = new Error("test error"); signer.call.rejects(testError); - // The error.context.error is the "test error" thrown by the signer.call. - await expect(chainProvider.readContract(TEST_READ_TX)).to.be.rejectedWith(TransactionReadError); + await expect(chainProvider.readContract(TEST_READ_TX)).to.rejected.then((error) => { + if (error instanceof TransactionReadError) { + expect(error.context.error).to.eq(testError); + expect(error.context.provider).to.deep.eq({ + url: coreSyncProvider.name, + blockNumber: coreSyncProvider.syncedBlockNumber, + lag: coreSyncProvider.lag, + synced: coreSyncProvider.synced, + metrics: { + reliability: coreSyncProvider.reliability, + priority: coreSyncProvider.priority, + }, + }); + } else { + expect.fail("Error should be a TransactionReadError"); + } + }); }); it("should execute with provider if no signer available", async () => {