diff --git a/src/ResponseListener.ts b/src/ResponseListener.ts index df94ce3..9eecc08 100644 --- a/src/ResponseListener.ts +++ b/src/ResponseListener.ts @@ -4,6 +4,7 @@ import { FunctionsRouterSource } from './v1_contract_sources' import type { BigNumber, providers } from 'ethers' +import { FunctionsTopics } from './events' import { FulfillmentCode, type FunctionsResponse } from './types' export class ResponseListener { @@ -88,7 +89,20 @@ export class ResponseListener { const check = async () => { const receipt = await this.provider.waitForTransaction(txHash, confirmations, timeoutMs) - const updatedId = receipt.logs[0].topics[1] + + // There must be logs in the receipt otherwise it's a chain that doesn't support logs or the tx was reverted + if (!receipt.logs) throw new Error('No logs found in the transaction receipt') + + // Find the RequestStart event in the logs + const requestStartLog = receipt.logs.find( + log => log.topics[0] === FunctionsTopics.RequestStart, + ) + + // Ensure the requestID exists in the logs + if (!requestStartLog) throw new Error('RequestStart event not found in the logs') + if (!requestStartLog.topics[1]) throw new Error('Request ID not found in the logs') + + const updatedId = requestStartLog.topics[1] if (updatedId !== requestId) { requestId = updatedId const response = await this.listenForResponse(requestId, timeoutMs) @@ -119,7 +133,7 @@ export class ResponseListener { } this.functionsRouter.on( - 'RequestProcessed', + { topics: [FunctionsTopics.RequestProcessed] }, ( requestId: string, _subscriptionId: BigNumber, diff --git a/src/SubscriptionManager.ts b/src/SubscriptionManager.ts index 174ff99..f184366 100644 --- a/src/SubscriptionManager.ts +++ b/src/SubscriptionManager.ts @@ -8,7 +8,7 @@ import { } from './v1_contract_sources' import type { Signer } from 'ethers' -import type { TransactionReceipt } from '@ethersproject/abstract-provider' +import type { TransactionReceipt, TransactionResponse } from '@ethersproject/abstract-provider' import type { SubConsumerConfig, @@ -21,6 +21,7 @@ import type { SubCreateConfig, EstimateCostConfig, } from './types' +import { FunctionsTopics } from './events' export class SubscriptionManager { private signer: Signer @@ -109,12 +110,24 @@ export class SubscriptionManager { : await this.functionsRouter.createSubscriptionWithConsumer( subCreateConfig.consumerAddress, ) - const createSubWithConsumerTxReceipt = await createSubWithConsumerTx.wait( + + const txReceipt: TransactionReceipt = await createSubWithConsumerTx.wait( subCreateConfig.txOptions?.confirmations, ) - const subscriptionId = createSubWithConsumerTxReceipt.events[0].args['subscriptionId'] + // Search through logs to find the topic that matches the SubscriptionCreated event + if (!txReceipt.logs) throw new Error('No logs present within transaction receipt') + + const createSubscriptionLog = txReceipt.logs.find( + log => log.topics[0] === FunctionsTopics.SubscriptionCreated, + ) + // Sanity checking, ensure that the SubscriptionCreated event was found in the log + if (!createSubscriptionLog) throw new Error('No SubscriptionCreated event found in logs') + if (!createSubscriptionLog.topics[1]) throw new Error('No subscriptionId found in logs') + + // The signature is SubscriptionCreated(uint64,address) so the subscriptionId is the second topic + const subscriptionId = createSubscriptionLog.topics[1] return Number(subscriptionId.toString()) } catch (error) { throw Error(`createSubscriptionWithConsumer failed\n${error}`) @@ -122,11 +135,26 @@ export class SubscriptionManager { } try { - const createSubTx = subCreateConfig?.txOptions?.overrides + const createSubTx: TransactionResponse = subCreateConfig?.txOptions?.overrides ? await this.functionsRouter.createSubscription(subCreateConfig?.txOptions.overrides) : await this.functionsRouter.createSubscription() - const createSubTxReceipt = await createSubTx.wait(subCreateConfig?.txOptions?.confirmations) - const subscriptionId = createSubTxReceipt.events[0].args['subscriptionId'] + + const createSubTxReceipt: TransactionReceipt = await createSubTx.wait( + subCreateConfig?.txOptions?.confirmations, + ) + + // Search through logs to find the topic that matches the SubscriptionCreated event + if (!createSubTxReceipt.logs) throw new Error('No logs present within transaction receipt') + const createSubscriptionLog = createSubTxReceipt.logs.find( + log => log.topics[0] === FunctionsTopics.SubscriptionCreated, + ) + + // Sanity checking, ensure that the SubscriptionCreated event was found in the log + if (!createSubscriptionLog) throw new Error('No SubscriptionCreated event found in logs') + if (!createSubscriptionLog.topics[1]) throw new Error('No subscriptionId found in logs') + + // The signature is SubscriptionCreated(uint64,address) so the subscriptionId is the second topic + const subscriptionId = createSubscriptionLog.topics[1] return Number(subscriptionId.toString()) } catch (error) { throw Error(`createSubscription failed\n${error}`) diff --git a/src/events.ts b/src/events.ts new file mode 100644 index 0000000..450bd1a --- /dev/null +++ b/src/events.ts @@ -0,0 +1,6 @@ +// This file contains a list of topics for events that are emitted by the Functions Contracts. +export const FunctionsTopics = { + SubscriptionCreated: '0x464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf', + RequestStart: '0xf67aec45c9a7ede407974a3e0c3a743dffeab99ee3f2d4c9a8144c2ebf2c7ec9', + RequestProcessed: '0x64778f26c70b60a8d7e29e2451b3844302d959448401c0535b768ed88c6b505e', +}