it('should default the missing attributes', async () => {
            await fc.assert(
                fc.asyncProperty(endpointArbitrary, dvnsArbitrary, async (eid, dvns) => {
                    const sortedDvns = [...dvns].sort((a, b) => a.localeCompare(b))
                    const ulnUserConfig: Uln302UlnUserConfig = {
                        requiredDVNs: dvns,
                    }
                    const ulnConfig: Uln302UlnConfig = {
                        requiredDVNs: dvns,
                        confirmations: BigInt(0),
                        optionalDVNThreshold: 0,
                        optionalDVNs: [],
                    }

                    // Let's check that both the sorted and the unsorted config produce the same transaction
                    const transactionsWithDefaults = await ulnSdk.setDefaultUlnConfig(eid, ulnUserConfig)
                    const transactionsWithFullConfig = await ulnSdk.setDefaultUlnConfig(eid, ulnConfig)
                    expect(transactionsWithDefaults).toEqual(transactionsWithFullConfig)

                    // And let's check that the encoding call is correct and the DVNs are sorted
                    expect(transactionsWithDefaults.data).toBe(
                        ulnSdk.contract.contract.interface.encodeFunctionData('setDefaultUlnConfigs', [
                            [
                                {
                                    eid,
                                    config: {
                                        confirmations: ulnConfig.confirmations,
                                        optionalDVNThreshold: ulnConfig.optionalDVNThreshold,
                                        optionalDVNs: ulnConfig.optionalDVNs,
                                        requiredDVNs: sortedDvns.map(addChecksum),
                                        requiredDVNCount: ulnConfig.requiredDVNs.length,
                                        optionalDVNCount: ulnConfig.optionalDVNs.length,
                                    },
                                },
                            ],
                        ])
                    )
                }),
                { numRuns: 20 }
            )
        })


unknown

it('should return true if the configs are identical', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    async (eid, oapp, ulnConfig) => {
                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(eid, oapp, ulnConfig, Uln302ConfigType.Receive)
                        ).resolves.toBeTruthy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return true if config has extra properties', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    fc.object(),
                    async (eid, oapp, ulnConfig, extra) => {
                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(
                                eid,
                                oapp,
                                {
                                    ...extra,
                                    ...ulnConfig,
                                },
                                Uln302ConfigType.Receive
                            )
                        ).resolves.toBeTruthy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return true if configs with defaults are identical', async () => {
            await fc.assert(
                fc.asyncProperty(endpointArbitrary, evmAddressArbitrary, dvnsArbitrary, async (eid, oapp, dvns) => {
                    const ulnUserConfig: Uln302UlnUserConfig = {
                        requiredDVNs: dvns,
                    }
                    const ulnConfig: Uln302UlnConfig = {
                        requiredDVNs: dvns,
                        optionalDVNs: [],
                        optionalDVNThreshold: 0,
                        confirmations: BigInt(0),
                    }

                    getAppUlnConfigSpy.mockReset()
                    getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                    await expect(
                        ulnSdk.hasAppUlnConfig(eid, oapp, ulnUserConfig, Uln302ConfigType.Receive)
                    ).resolves.toBeTruthy()
                }),
                { numRuns: 20 }
            )
        })


it('should return false if configs with defaults are not identical', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    async (eid, oapp, ulnConfig) => {
                        // We only want to test againsts configs that are not
                        // equal to the default config
                        fc.pre(
                            ulnConfig.confirmations !== BigInt(0) ||
                                ulnConfig.optionalDVNThreshold !== 0 ||
                                ulnConfig.optionalDVNs.length !== 0
                        )

                        const ulnUserConfig: Uln302UlnUserConfig = {
                            requiredDVNs: ulnConfig.requiredDVNs,
                        }

                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(eid, oapp, ulnUserConfig, Uln302ConfigType.Receive)
                        ).resolves.toBeFalsy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return true if the configs are identical except for the dvn casing', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    async (eid, oapp, ulnConfig) => {
                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(
                                eid,
                                oapp,
                                {
                                    ...ulnConfig,
                                    requiredDVNs: ulnConfig.requiredDVNs.map(addChecksum),
                                    optionalDVNs: ulnConfig.optionalDVNs.map(addChecksum),
                                },
                                Uln302ConfigType.Receive
                            )
                        ).resolves.toBeTruthy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return true if the configs are identical except for the dvn sorting and casing', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    async (eid, oapp, ulnConfig) => {
                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(
                                eid,
                                oapp,
                                {
                                    ...ulnConfig,
                                    requiredDVNs: ulnConfig.requiredDVNs.map(addChecksum).sort(compareBytes32Ascending),
                                    optionalDVNs: ulnConfig.optionalDVNs.map(addChecksum).sort(compareBytes32Ascending),
                                },
                                Uln302ConfigType.Receive
                            )
                        ).resolves.toBeTruthy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return false if the confirmations are different', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    fc.bigInt(),
                    async (eid, oapp, ulnConfig, confirmations) => {
                        fc.pre(confirmations !== ulnConfig.confirmations)

                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(
                                eid,
                                oapp,
                                {
                                    ...ulnConfig,
                                    confirmations,
                                },
                                Uln302ConfigType.Receive
                            )
                        ).resolves.toBeFalsy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return false if the optionalDVNThresholds are different', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    fc.integer(),
                    async (eid, oapp, ulnConfig, optionalDVNThreshold) => {
                        fc.pre(optionalDVNThreshold !== ulnConfig.optionalDVNThreshold)

                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(
                                eid,
                                oapp,
                                {
                                    ...ulnConfig,
                                    optionalDVNThreshold,
                                },
                                Uln302ConfigType.Receive
                            )
                        ).resolves.toBeFalsy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return false if the requiredDVNs are different', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    dvnsArbitrary,
                    async (eid, oapp, ulnConfig, extraRequiredDVNs) => {
                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(
                                eid,
                                oapp,
                                {
                                    ...ulnConfig,
                                    requiredDVNs: [...ulnConfig.requiredDVNs, ...extraRequiredDVNs],
                                },
                                Uln302ConfigType.Receive
                            )
                        ).resolves.toBeFalsy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return false if the optionalDVNs are different', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    ulnConfigArbitrary,
                    dvnsArbitrary,
                    async (eid, oapp, ulnConfig, extraOptionalDVNs) => {
                        getAppUlnConfigSpy.mockReset()
                        getAppUlnConfigSpy.mockResolvedValue(ulnConfig)

                        await expect(
                            ulnSdk.hasAppUlnConfig(
                                eid,
                                oapp,
                                {
                                    ...ulnConfig,
                                    optionalDVNs: [...ulnConfig.optionalDVNs, ...extraOptionalDVNs],
                                },
                                Uln302ConfigType.Receive
                            )
                        ).resolves.toBeFalsy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return true if the configs are identical', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    executorConfigArbitrary,
                    async (eid, oapp, executorConfig) => {
                        getAppExecutorConfigSpy.mockReset()
                        getAppExecutorConfigSpy.mockResolvedValue(executorConfig)

                        await expect(ulnSdk.hasAppExecutorConfig(eid, oapp, executorConfig)).resolves.toBeTruthy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return true if the configs are identical except for the executor casing', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    executorConfigArbitrary,
                    async (eid, oapp, executorConfig) => {
                        getAppExecutorConfigSpy.mockReset()
                        getAppExecutorConfigSpy.mockResolvedValue(executorConfig)

                        await expect(
                            ulnSdk.hasAppExecutorConfig(eid, oapp, {
                                ...executorConfig,
                                executor: addChecksum(executorConfig.executor),
                            })
                        ).resolves.toBeTruthy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should return true if config has extra properties', async () => {
            await fc.assert(
                fc.asyncProperty(
                    endpointArbitrary,
                    evmAddressArbitrary,
                    executorConfigArbitrary,
                    fc.object(),
                    async (eid, oapp, executorConfig, extra) => {
                        getAppExecutorConfigSpy.mockReset()
                        getAppExecutorConfigSpy.mockResolvedValue(executorConfig)

                        await expect(
                            ulnSdk.hasAppExecutorConfig(eid, oapp, {
                                ...extra,
                                ...executorConfig,
                            })
                        ).resolves.toBeTruthy()
                    }
                ),
                { numRuns: 20 }
            )
        })


it('should reject if the address is a zeroish address', async () => {
            await fc.assert(
                fc.asyncProperty(pointArbitrary, zeroishAddressArbitrary, async (point, address) => {
                    const provider = new JsonRpcProvider()
                    const sdk = new EndpointV2(provider, point)

                    await expect(sdk.getUln302SDK(address)).rejects.toThrow(
                        /Uln302 cannot be instantiated: Uln302 address cannot be a zero value for Endpoint/
                    )
                })
            )
        })


it('should return a ULN302 if the address is a non-zeroish address', async () => {
            await fc.assert(
                fc.asyncProperty(pointArbitrary, evmAddressArbitrary, async (point, address) => {
                    fc.pre(!isZero(address))

                    const provider = new JsonRpcProvider()

                    const sdk = new EndpointV2(provider, point)
                    const uln302 = await sdk.getUln302SDK(address)

                    expect(uln302).toBeInstanceOf(Uln302)
                    expect(uln302.point).toEqual({ eid: point.eid, address })
                })
            )
        })


it('should return a tuple if the call succeeds', async () => {
            await fc.assert(
                fc.asyncProperty(
                    pointArbitrary,
                    endpointArbitrary,
                    evmAddressArbitrary,
                    evmAddressArbitrary,
                    fc.boolean(),
                    async (point, eid, oappAddress, libraryAddress, isDefault) => {
                        const provider = new JsonRpcProvider()
                        const sdk = new EndpointV2(provider, point)

                        jest.spyOn(provider, 'call').mockResolvedValue(
                            sdk.contract.contract.interface.encodeFunctionResult('getReceiveLibrary', [
                                libraryAddress,
                                isDefault,
                            ])
                        )

                        await expect(sdk.getReceiveLibrary(oappAddress, eid)).resolves.toEqual([
                            addChecksum(libraryAddress),
                            isDefault,
                        ])
                    }
                )
            )
        })


it('should return undefined as the default lib if the call fails with LZ_DefaultReceiveLibUnavailable', async () => {
            await fc.assert(
                fc.asyncProperty(
                    pointArbitrary,
                    endpointArbitrary,
                    evmAddressArbitrary,
                    async (point, eid, oappAddress) => {
                        const provider = new JsonRpcProvider()
                        const sdk = new EndpointV2(provider, point)
                        // The LZ_DefaultReceiveLibUnavailable error
                        const error = { data: '0x78e84d06' }

                        jest.spyOn(provider, 'call').mockRejectedValue(error)

                        await expect(sdk.getReceiveLibrary(oappAddress, eid)).resolves.toEqual([undefined, true])
                    }
                )
            )
        })


it('should reject if call fails but not with LZ_DefaultReceiveLibUnavailable', async () => {
            await fc.assert(
                fc.asyncProperty(
                    pointArbitrary,
                    endpointArbitrary,
                    evmAddressArbitrary,
                    async (point, eid, oappAddress) => {
                        const provider = new JsonRpcProvider()
                        const sdk = new EndpointV2(provider, point)
                        const error = { data: '0x86957466' }

                        jest.spyOn(provider, 'call').mockRejectedValue(error)

                        await expect(sdk.getReceiveLibrary(oappAddress, eid)).rejects.toEqual(error)
                    }
                )
            )
        })


it('should return an address if the call succeeds', async () => {
            await fc.assert(
                fc.asyncProperty(
                    pointArbitrary,
                    endpointArbitrary,
                    evmAddressArbitrary,
                    evmAddressArbitrary,
                    async (point, eid, oappAddress, libraryAddress) => {
                        const provider = new JsonRpcProvider()
                        const sdk = new EndpointV2(provider, point)

                        jest.spyOn(provider, 'call').mockResolvedValue(
                            sdk.contract.contract.interface.encodeFunctionResult('getSendLibrary', [libraryAddress])
                        )

                        await expect(sdk.getSendLibrary(oappAddress, eid)).resolves.toEqual(addChecksum(libraryAddress))
                    }
                )
            )
        })


it('should return undefined if the call fails with LZ_DefaultSendLibUnavailable', async () => {
            await fc.assert(
                fc.asyncProperty(
                    pointArbitrary,
                    endpointArbitrary,
                    evmAddressArbitrary,
                    async (point, eid, oappAddress) => {
                        const provider = new JsonRpcProvider()
                        const sdk = new EndpointV2(provider, point)
                        // The LZ_DefaultReceiveLibUnavailable error
                        const error = { data: '0x6c1ccdb5' }

                        jest.spyOn(provider, 'call').mockRejectedValue(error)

                        await expect(sdk.getSendLibrary(oappAddress, eid)).resolves.toEqual(undefined)
                    }
                )
            )
        })


it('should reject if call fails but not with LZ_DefaultSendLibUnavailable', async () => {
            await fc.assert(
                fc.asyncProperty(
                    pointArbitrary,
                    endpointArbitrary,
                    evmAddressArbitrary,
                    async (point, eid, oappAddress) => {
                        const provider = new JsonRpcProvider()
                        const sdk = new EndpointV2(provider, point)
                        const error = { data: '0x86957466' }

                        jest.spyOn(provider, 'call').mockRejectedValue(error)

                        await expect(sdk.getSendLibrary(oappAddress, eid)).rejects.toEqual(error)
                    }
                )
            )
        })


it('should call onStart & onSuccess when the callback resolves', async () => {
                await fc.assert(
                    fc.asyncProperty(fc.array(fc.anything()), fc.anything(), async (args, returnValue) => {
                        const fn = jest.fn().mockResolvedValue(returnValue)
                        const onStart = jest.fn().mockImplementation((logger: Logger) => logger.debug(`onStart`))
                        const onSuccess = jest.fn().mockImplementation((logger: Logger) => logger.debug(`onSuccess`))

                        await expect(withAsyncLogger(fn, { onStart, onSuccess })(...args)).resolves.toBe(returnValue)

                        expect(onStart).toHaveBeenCalledWith(expect.anything(), args)
                        expect(onSuccess).toHaveBeenCalledWith(expect.anything(), args, returnValue)
                        expect(onStart).toHaveBeenCalledBefore(onSuccess)
                    })
                )
            })


it('should call onStart & onFailure when callback rejects', async () => {
                await fc.assert(
                    fc.asyncProperty(fc.array(fc.anything()), fc.anything(), async (args, error) => {
                        const fn = jest.fn().mockRejectedValue(error)
                        const onStart = jest.fn().mockImplementation((logger: Logger) => logger.debug(`onStart`))
                        const onError = jest.fn().mockImplementation((logger: Logger) => logger.debug(`onSuccess`))

                        await expect(withAsyncLogger(fn, { onStart, onError })(...args)).rejects.toBe(error)

                        expect(onStart).toHaveBeenCalledWith(expect.anything(), args)
                        expect(onError).toHaveBeenCalledWith(expect.anything(), args, error)
                        expect(onStart).toHaveBeenCalledBefore(onError)
                    })
                )
            })


it('should resolve if onSuccess call throws', async () => {
                await fc.assert(
                    fc.asyncProperty(
                        fc.array(fc.anything()),
                        fc.anything(),
                        fc.anything(),
                        async (args, returnValue, loggerError) => {
                            const fn = jest.fn().mockResolvedValue(returnValue)
                            const onStart = jest.fn().mockImplementation((logger: Logger) => logger.debug(`onStart`))
                            const onSuccess = jest.fn().mockImplementation(() => {
                                throw loggerError
                            })

                            await expect(withAsyncLogger(fn, { onStart, onSuccess })(...args)).resolves.toBe(
                                returnValue
                            )

                            expect(onStart).toHaveBeenCalledWith(expect.anything(), args)
                            expect(onSuccess).toHaveBeenCalledWith(expect.anything(), args, returnValue)
                            expect(onStart).toHaveBeenCalledBefore(onSuccess)
                        }
                    )
                )
            })


it('should reject with the original error if onError callback throws', async () => {
                await fc.assert(
                    fc.asyncProperty(
                        fc.array(fc.anything()),
                        fc.anything(),
                        fc.anything(),
                        async (args, error, loggerError) => {
                            const fn = jest.fn().mockRejectedValue(error)
                            const onStart = jest.fn().mockImplementation((logger: Logger) => logger.debug(`onStart`))
                            const onError = jest.fn().mockImplementation(() => {
                                throw loggerError
                            })

                            await expect(withAsyncLogger(fn, { onStart, onError })(...args)).rejects.toBe(error)

                            expect(onStart).toHaveBeenCalledWith(expect.anything(), args)
                            expect(onError).toHaveBeenCalledWith(expect.anything(), args, error)
                            expect(onStart).toHaveBeenCalledBefore(onError)
                        }
                    )
                )
            })


it('should return an empty array when called with an array of nulls and undefineds', () => {
            fc.assert(
                fc.property(fc.array(nullableArbitrary), (transactions) => {
                    expect(flattenTransactions(transactions)).toEqual([])
                })
            )
        })


it('should remove any nulls or undefineds', () => {
            fc.assert(
                fc.property(fc.array(fc.oneof(transactionArbitrary, nullableArbitrary)), (transactions) => {
                    const flattened = flattenTransactions(transactions)

                    for (const transaction of flattened) {
                        expect(transaction).not.toBeNull()
                        expect(transaction).not.toBeUndefined()
                    }
                })
            )
        })


it('should flatten any arrays', () => {
            fc.assert(
                fc.property(
                    fc.array(fc.oneof(transactionArbitrary, nullableArbitrary, fc.array(transactionArbitrary))),
                    (transactions) => {
                        const flattened = flattenTransactions(transactions)

                        for (const transaction of flattened) {
                            expect(transaction).not.toBeInstanceOf(Array)
                        }
                    }
                )
            )
        })


it('should return a map with containing all the transactions passed in', () => {
            fc.assert(
                fc.property(fc.array(transactionArbitrary), (transactions) => {
                    const grouped = groupTransactionsByEid(transactions)

                    for (const transaction of transactions) {
                        expect(grouped.get(transaction.point.eid)).toContain(transaction)
                    }
                })
            )
        })


it('should preserve the order of transaction per group', () => {
            fc.assert(
                fc.property(fc.array(transactionArbitrary), (transactions) => {
                    const grouped = groupTransactionsByEid(transactions)

                    for (const transactionsForEid of grouped.values()) {
                        // Here we want to make sure that within a group of transactions,
                        // no transactions have changed order
                        //
                        // The logic here goes something like this:
                        // - We look for the indices of transactions from the grouped array in the original array
                        // - If two transactions swapped places, this array would then contain an inversion
                        //   (transaction at an earlier index in the grouped array would appear after a transaction
                        //   at a later index). So what we do is remove the inversions by sorting the array of indices
                        //   and make sure this sorted array matches the original one
                        const transactionIndices = transactionsForEid.map((t) => transactions.indexOf(t))
                        const sortedTransactionIndices = transactionIndices.slice().sort()

                        expect(transactionIndices).toEqual(sortedTransactionIndices)
                    }
                })
            )
        })


it('should return the first transaction as failed if createSigner fails', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            const signerFactory: OmniSignerFactory = jest
                                .fn()
                                .mockImplementation((eid: EndpointId) => Promise.reject(new Error(`So sorry ${eid}`)))
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            const grouped = groupTransactionsByEid(transactions)

                            // We expect none of the transactions to go through
                            expect(successful).toEqual([])
                            // We expect the errors to contain the first transaction and the wrapped error from the signer factory
                            expect(errors).toEqual(
                                Array.from(grouped.entries()).map(([eid, transactions]) => ({
                                    error: new Error(
                                        `Failed to create a signer for ${formatEid(eid)}: ${new Error(`So sorry ${eid}`)}`
                                    ),
                                    transaction: transactions[0],
                                }))
                            )
                            // And we expect all the transactions to be pending
                            expect(pending).toContainAllValues(
                                Array.from(grouped.entries()).flatMap(([, transactions]) => transactions)
                            )
                        })
                    )
                })


it('should return all successful transactions if they all go through', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const successfulWait = jest.fn().mockResolvedValue(receipt)
                            const successfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: successfulWait,
                            }

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSend = jest.fn().mockResolvedValue(successfulResponse)
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest.fn().mockResolvedValue({ signAndSend, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toContainAllValues(
                                transactions.map((transaction) => ({ transaction, receipt }))
                            )
                            expect(errors).toEqual([])
                            expect(pending).toEqual([])

                            // What needs to match though is the order of successful transactions within groups
                            //
                            // For that we group the successful transactions and make sure those are equal to the grouped original transactions
                            const groupedSuccessful = groupTransactionsByEid(
                                successful.map(({ transaction }) => transaction)
                            )
                            expect(groupedSuccessful).toEqual(groupTransactionsByEid(transactions))

                            // We also check that the signer factory has been called with the eids
                            for (const transaction of transactions) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        })
                    )
                })


it('should bail on the first submission error', async () => {
                    await fc.assert(
                        fc.asyncProperty(
                            fc.array(transactionArbitrary),
                            transactionArbitrary,
                            fc.array(transactionArbitrary),
                            async (firstBatch, failedTransaction, secondBatch) => {
                                // We'll prepare some mock objects for this test
                                // to mock the transaction responses and receipts
                                const error = new Error('Failed transaction')
                                const receipt = { transactionHash: '0x0' }
                                const successfulWait = jest.fn().mockResolvedValue(receipt)
                                const successfulResponse: OmniTransactionResponse = {
                                    transactionHash: '0x0',
                                    wait: successfulWait,
                                }

                                // In order to resolve the good ones and reject the bad ones
                                // we'll prepare a map between a transaction and its response
                                //
                                // This map relies on the fact that we are passing the transaction object without modifying it
                                // so the objects are referentially equal
                                const implementations: Map<OmniTransaction, Promise<unknown>> = new Map([
                                    ...firstBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                    ...secondBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                    [failedTransaction, Promise.reject(error)],
                                ])

                                const expectedSuccessful = [
                                    // The first batch should all go through
                                    ...firstBatch,
                                    // The transactions that are not on the chain affected by the failed transaction should also pass
                                    ...secondBatch.filter(({ point }) => point.eid !== failedTransaction.point.eid),
                                ]

                                const expectedPending = secondBatch.filter(
                                    ({ point }) => point.eid === failedTransaction.point.eid
                                )

                                // Our signAndSend will then use the map to resolve/reject transactions
                                const signAndSend = jest.fn().mockImplementation((t) => implementations.get(t))
                                const sign = jest.fn().mockRejectedValue('Oh god no')
                                const signerFactory: OmniSignerFactory = jest
                                    .fn()
                                    .mockResolvedValue({ signAndSend, sign })
                                const signAndSendTransactions = createSignAndSend(signerFactory)

                                // Now we send all the transactions to the flow and observe the output
                                const transactions = [...firstBatch, failedTransaction, ...secondBatch]
                                const [successful, errors, pending] = await signAndSendTransactions(transactions)

                                // Since we are executing groups of transactions in parallel,
                                // in general the order of successful transaction will not match the order of input transactions
                                expect(successful).toContainAllValues(
                                    expectedSuccessful.map((transaction) => ({ transaction, receipt }))
                                )
                                expect(errors).toEqual([{ transaction: failedTransaction, error }])
                                expect(pending).toEqual([failedTransaction, ...expectedPending])

                                // What needs to match though is the order of successful transactions within groups
                                //
                                // For that we group the successful transactions and make sure those are equal to the grouped original transactions
                                const groupedSuccessful = groupTransactionsByEid(
                                    successful.map(({ transaction }) => transaction)
                                )
                                expect(groupedSuccessful).toEqual(groupTransactionsByEid(expectedSuccessful))

                                // We also check that the signer factory has been called with the eids
                                expect(signerFactory).toHaveBeenCalledWith(failedTransaction.point.eid)
                                for (const transaction of firstBatch) {
                                    expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                                }
                            }
                        )
                    )
                })


it('should call onProgress for every successful transaction', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const successfulWait = jest.fn().mockResolvedValue(receipt)
                            const successfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: successfulWait,
                            }

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSend = jest.fn().mockResolvedValue(successfulResponse)
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest.fn().mockResolvedValue({ signAndSend, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            const handleProgress = jest.fn()
                            const [successful] = await signAndSendTransactions(transactions, handleProgress)

                            // We check whether onProgress has been called for every transaction
                            for (const [index, transaction] of successful.entries()) {
                                expect(handleProgress).toHaveBeenNthCalledWith(
                                    index + 1,
                                    // We expect the transaction in question to be passed
                                    transaction,
                                    // As well as the list of all the successful transactions so far
                                    successful.slice(0, index + 1)
                                )
                            }
                        })
                    )
                })


it('should bail on the first wait error', async () => {
                await fc.assert(
                    fc.asyncProperty(
                        fc.array(transactionArbitrary),
                        transactionArbitrary,
                        fc.array(transactionArbitrary),
                        async (firstBatch, failedTransaction, secondBatch) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const error = new Error('Failed transaction')
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const successfulWait = jest.fn().mockResolvedValue(receipt)
                            const successfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: successfulWait,
                            }

                            // Our unsuccessful wait will throw an error
                            const unsuccessfulWait = jest.fn().mockRejectedValue(error)
                            const unsuccessfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: unsuccessfulWait,
                            }

                            // In order to resolve the good ones and reject the bad ones
                            // we'll prepare a map between a transaction and its response
                            //
                            // This map relies on the fact that we are passing the transaction object without modifying it
                            // so the objects are referentially equal
                            const implementations: Map<OmniTransaction, Promise<unknown>> = new Map([
                                ...firstBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                ...secondBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                [failedTransaction, Promise.resolve(unsuccessfulResponse)],
                            ])

                            const expectedSuccessful = [
                                // The first batch should all go through
                                ...firstBatch,
                                // The transactions that are not on the chain affected by the failed transaction should also pass
                                ...secondBatch.filter(({ point }) => point.eid !== failedTransaction.point.eid),
                            ]

                            const expectedPending = secondBatch.filter(
                                ({ point }) => point.eid === failedTransaction.point.eid
                            )

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSend = jest.fn().mockImplementation((t) => implementations.get(t))
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest.fn().mockResolvedValue({ signAndSend, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const transactions = [...firstBatch, failedTransaction, ...secondBatch]
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toContainAllValues(
                                expectedSuccessful.map((transaction) => ({ transaction, receipt }))
                            )
                            expect(errors).toEqual([{ transaction: failedTransaction, error }])
                            expect(pending).toEqual([failedTransaction, ...expectedPending])

                            // What needs to match though is the order of successful transactions within groups
                            //
                            // For that we group the successful transactions and make sure those are equal to the grouped original transactions
                            const groupedSuccessful = groupTransactionsByEid(
                                successful.map(({ transaction }) => transaction)
                            )
                            expect(groupedSuccessful).toEqual(groupTransactionsByEid(expectedSuccessful))

                            // We also check that the signer factory has been called with the eids
                            expect(signerFactory).toHaveBeenCalledWith(failedTransaction.point.eid)
                            for (const transaction of firstBatch) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        }
                    )
                )
            })


it('should not bail on the first wait error', async () => {
                await fc.assert(
                    fc.asyncProperty(
                        fc.array(transactionArbitrary),
                        transactionArbitrary,
                        fc.array(transactionArbitrary),
                        async (firstBatch, failedTransaction, secondBatch) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const error = new Error('Failed transaction')
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const successfulWait = jest.fn().mockResolvedValue(receipt)
                            const successfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: successfulWait,
                            }

                            // Our unsuccessful wait will throw an error
                            const unsuccessfulWait = jest.fn().mockRejectedValue(error)
                            const unsuccessfulResponse: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait: unsuccessfulWait,
                            }

                            // In order to resolve the good ones and reject the bad ones
                            // we'll prepare a map between a transaction and its response
                            //
                            // This map relies on the fact that we are passing the transaction object without modifying it
                            // so the objects are referentially equal
                            const implementations: Map<OmniTransaction, Promise<unknown>> = new Map([
                                ...firstBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                ...secondBatch.map((t) => [t, Promise.resolve(successfulResponse)] as const),
                                [failedTransaction, Promise.resolve(unsuccessfulResponse)],
                            ])

                            const expectedSuccessful = [
                                // The first batch should all go through
                                ...firstBatch,
                                // The second batch should all go through since they all were submitted and will all be mined
                                ...secondBatch,
                            ]

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSend = jest.fn().mockImplementation((t) => implementations.get(t))
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest.fn().mockResolvedValue({ signAndSend, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const transactions = [...firstBatch, failedTransaction, ...secondBatch]
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toContainAllValues(
                                expectedSuccessful.map((transaction) => ({ transaction, receipt }))
                            )
                            expect(errors).toEqual([{ transaction: failedTransaction, error }])
                            expect(pending).toEqual([failedTransaction])

                            // What needs to match though is the order of successful transactions within groups
                            //
                            // For that we group the successful transactions and make sure those are equal to the grouped original transactions
                            const groupedSuccessful = groupTransactionsByEid(
                                successful.map(({ transaction }) => transaction)
                            )
                            expect(groupedSuccessful).toEqual(groupTransactionsByEid(expectedSuccessful))

                            // We also check that the signer factory has been called with the eids
                            expect(signerFactory).toHaveBeenCalledWith(failedTransaction.point.eid)
                            for (const transaction of firstBatch) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        }
                    )
                )
            })


it('should only return errors if the submission fails', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const error = new Error('Failed transaction')

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSendBatch = jest.fn().mockRejectedValue(error)
                            const signAndSend = jest.fn().mockRejectedValue('Oh god no')
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest
                                .fn()
                                .mockResolvedValue({ signAndSend, signAndSendBatch, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toEqual([])
                            expect(errors).toContainAllValues(
                                transactions.map((transaction) => ({ transaction, error }))
                            )
                            expect(pending).toContainAllValues(transactions)

                            // We also check that the signer factory has been called with the eids
                            for (const transaction of transactions) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        })
                    )
                })


it('should only return errors if the waiting fails', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            // We'll prepare some mock objects for this test
                            // to mock the transaction responses and receipts
                            const error = new Error('Failed transaction')
                            // Our unsuccessful wait will throw an error
                            const wait = jest.fn().mockRejectedValue(error)
                            const response: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait,
                            }

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSendBatch = jest.fn().mockResolvedValue(response)
                            const signAndSend = jest.fn().mockRejectedValue('Oh god no')
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest
                                .fn()
                                .mockResolvedValue({ signAndSend, signAndSendBatch, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toEqual([])
                            expect(errors).toContainAllValues(
                                transactions.map((transaction) => ({ transaction, error }))
                            )
                            expect(pending).toContainAllValues(transactions)

                            // We also check that the signer factory has been called with the eids
                            for (const transaction of transactions) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        })
                    )
                })


it('should only return successes if waiting succeeds', async () => {
                    await fc.assert(
                        fc.asyncProperty(fc.array(transactionArbitrary), async (transactions) => {
                            const receipt = { transactionHash: '0x0' }

                            // Our successful wait will produce a receipt
                            const wait = jest.fn().mockResolvedValue(receipt)
                            const response: OmniTransactionResponse = {
                                transactionHash: '0x0',
                                wait,
                            }

                            // Our signAndSend will then use the map to resolve/reject transactions
                            const signAndSendBatch = jest.fn().mockResolvedValue(response)
                            const signAndSend = jest.fn().mockRejectedValue('Oh god no')
                            const sign = jest.fn().mockRejectedValue('Oh god no')
                            const signerFactory: OmniSignerFactory = jest
                                .fn()
                                .mockResolvedValue({ signAndSend, signAndSendBatch, sign })
                            const signAndSendTransactions = createSignAndSend(signerFactory)

                            // Now we send all the transactions to the flow and observe the output
                            const [successful, errors, pending] = await signAndSendTransactions(transactions)

                            // Since we are executing groups of transactions in parallel,
                            // in general the order of successful transaction will not match the order of input transactions
                            expect(successful).toContainAllValues(
                                transactions.map((transaction) => ({ transaction, receipt }))
                            )
                            expect(errors).toEqual([])
                            expect(pending).toEqual([])

                            // We also check that the signer factory has been called with the eids
                            for (const transaction of transactions) {
                                expect(signerFactory).toHaveBeenCalledWith(transaction.point.eid)
                            }
                        })
                    )
                })


it('should parse successfully', () => {
                fc.assert(
                    fc.property(pointArbitrary, good, (point, config) => {
                        expect(schema.safeParse({ point, config }).success).toBeTruthy()
                    })
                )
            })


it('should not parse', () => {
                fc.assert(
                    fc.property(pointArbitrary, bad, (point, config) => {
                        expect(schema.safeParse({ point, config }).success).toBeFalsy()
                    })
                )
            })


it('should parse successfully', () => {
                fc.assert(
                    fc.property(vectorArbitrary, good, (vector, config) => {
                        expect(schema.safeParse({ vector, config }).success).toBeTruthy()
                    })
                )
            })


it('should not parse', () => {
                fc.assert(
                    fc.property(vectorArbitrary, bad, (vector, config) => {
                        expect(schema.safeParse({ vector, config }).success).toBeFalsy()
                    })
                )
            })


it('should work for non-negative bigints', () => {
            fc.assert(
                fc.property(fc.bigInt({ min: BigInt(0) }), (value) => {
                    expect(UIntBigIntSchema.parse(value)).toBe(value)
                })
            )
        })


it('should not work for negative bigints', () => {
            fc.assert(
                fc.property(fc.bigInt({ max: BigInt(-1) }), (value) => {
                    expect(() => UIntBigIntSchema.parse(value)).toThrow()
                })
            )
        })


it('should work for non-negative bigint strings', () => {
            fc.assert(
                fc.property(fc.bigInt({ min: BigInt(0) }), (value) => {
                    expect(UIntBigIntSchema.parse(String(value))).toBe(value)
                })
            )
        })


it('should not work for negative bigint strings', () => {
            fc.assert(
                fc.property(fc.bigInt({ max: BigInt(-1) }), (value) => {
                    expect(() => UIntBigIntSchema.parse(String(value))).toThrow()
                })
            )
        })


it('should work for non-negative integers', () => {
            fc.assert(
                fc.property(fc.integer({ min: 0 }), (value) => {
                    expect(UIntBigIntSchema.parse(value)).toBe(BigInt(value))
                })
            )
        })


it('should not work for negative integers', () => {
            fc.assert(
                fc.property(fc.integer({ max: -1 }), (value) => {
                    expect(() => UIntBigIntSchema.parse(value)).toThrow()
                })
            )
        })


it('should work for non-negative integer strings', () => {
            fc.assert(
                fc.property(fc.integer({ min: 0 }), (value) => {
                    expect(UIntBigIntSchema.parse(String(value))).toBe(BigInt(value))
                })
            )
        })


it('should not work for negative integer strings', () => {
            fc.assert(
                fc.property(fc.integer({ max: -1 }), (value) => {
                    expect(() => UIntBigIntSchema.parse(String(value))).toThrow()
                })
            )
        })


it('should throw a ZodError for invalid values', () => {
            fc.assert(
                fc.property(fc.anything(), (value) => {
                    let isValid = false

                    try {
                        isValid = UIntBigIntSchema.safeParse(value).success
                    } catch {
                        // This catch block is designed to catch errors caused by messed up standard methods
                        // like toString or toValue that fast check throws at us
                    }

                    fc.pre(!isValid)

                    // Here we expect that whatever we got, we'll not receive a SyntaxError
                    // (coming from a BigInt() constructor) but a ZodError
                    expect(() => UIntBigIntSchema.parse(value)).toThrow(ZodError)
                })
            )
        })


it('should work for non-negative integers', () => {
            fc.assert(
                fc.property(fc.integer({ min: 0 }), (value) => {
                    expect(UIntNumberSchema.parse(value)).toBe(value)
                })
            )
        })


it('should not work for negative integers', () => {
            fc.assert(
                fc.property(fc.integer({ max: -1 }), (value) => {
                    expect(() => UIntNumberSchema.parse(value)).toThrow()
                })
            )
        })


it('should work for non-negative integer strings', () => {
            fc.assert(
                fc.property(fc.integer({ min: 0 }), (value) => {
                    expect(UIntNumberSchema.parse(String(value))).toBe(value)
                })
            )
        })


it('should not work for negative integer strings', () => {
            fc.assert(
                fc.property(fc.integer({ max: -1 }), (value) => {
                    expect(() => UIntNumberSchema.parse(String(value))).toThrow()
                })
            )
        })


it('should throw a ZodError for invalid values', () => {
            fc.assert(
                fc.property(fc.anything(), (value) => {
                    let isValid = false

                    try {
                        isValid = UIntNumberSchema.safeParse(value).success
                    } catch {
                        // This catch block is designed to catch errors caused by messed up standard methods
                        // like toString or toValue that fast check throws at us
                    }

                    fc.pre(!isValid)

                    // Here we expect that whatever we got, we'll not receive a SyntaxError
                    // (coming from a BigInt() constructor) but a ZodError
                    expect(() => UIntNumberSchema.parse(value)).toThrow(ZodError)
                })
            )
        })


it('should be true for referentially equal vector', () => {
                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(arePointsEqual(point, point)).toBeTruthy()
                    })
                )
            })


it('should be true for value equal vector', () => {
                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(arePointsEqual(point, { ...point })).toBeTruthy()
                    })
                )
            })


it("should be false when addresses don't match", () => {
                fc.assert(
                    fc.property(pointArbitrary, addressArbitrary, (point, address) => {
                        fc.pre(point.address !== address)

                        expect(arePointsEqual(point, { ...point, address })).toBeFalsy()
                    })
                )
            })


it("should be false when endpoint IDs don't match", () => {
                fc.assert(
                    fc.property(pointArbitrary, endpointArbitrary, (point, eid) => {
                        fc.pre(point.eid !== eid)

                        expect(arePointsEqual(point, { ...point, eid })).toBeFalsy()
                    })
                )
            })


it("should be false when contract names don't match", () => {
                fc.assert(
                    fc.property(pointArbitrary, nullableArbitrary(fc.string()), (point, contractName) => {
                        fc.pre(point.contractName !== contractName)

                        expect(arePointsEqual(point, { ...point, contractName })).toBeFalsy()
                    })
                )
            })


it('should be true for referentially equal vector', () => {
                fc.assert(
                    fc.property(vectorArbitrary, (vector) => {
                        expect(areVectorsEqual(vector, vector)).toBeTruthy()
                    })
                )
            })


it('should be true for value equal vector', () => {
                fc.assert(
                    fc.property(vectorArbitrary, (vector) => {
                        expect(areVectorsEqual(vector, { ...vector })).toBeTruthy()
                    })
                )
            })


it("should be false when from point doesn't match", () => {
                fc.assert(
                    fc.property(vectorArbitrary, pointArbitrary, (vector, from) => {
                        fc.pre(!arePointsEqual(vector.from, from))

                        expect(areVectorsEqual(vector, { ...vector, from })).toBeFalsy()
                    })
                )
            })


it("should be false when to point doesn't match", () => {
                fc.assert(
                    fc.property(vectorArbitrary, pointArbitrary, (vector, to) => {
                        fc.pre(!arePointsEqual(vector.from, to))

                        expect(areVectorsEqual(vector, { ...vector, to })).toBeFalsy()
                    })
                )
            })


it('should return true if the eids match', () => {
                fc.assert(
                    fc.property(pointArbitrary, pointArbitrary, (pointA, pointB) => {
                        expect(areSameEndpoint(pointA, { ...pointB, eid: pointA.eid })).toBeTruthy()
                    })
                )
            })


it('should return false if the eids differ', () => {
                fc.assert(
                    fc.property(pointArbitrary, pointArbitrary, (pointA, pointB) => {
                        fc.pre(pointA.eid !== pointB.eid)

                        expect(areSameEndpoint(pointA, pointB)).toBeFalsy()
                    })
                )
            })


it('should produce identical serialized values if the vector match', () => {
                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(serializePoint(point)).toBe(serializePoint({ ...point }))
                    })
                )
            })


it("should produce different serialized values if the vector don't match", () => {
                fc.assert(
                    fc.property(pointArbitrary, pointArbitrary, (pointA, pointB) => {
                        fc.pre(!arePointsEqual(pointA, pointB))

                        expect(serializePoint(pointA)).not.toBe(serializePoint(pointB))
                    })
                )
            })


it('should produce identical serialized values if the vector match', () => {
                fc.assert(
                    fc.property(vectorArbitrary, (vector) => {
                        expect(serializeVector(vector)).toBe(serializeVector({ ...vector }))
                    })
                )
            })


it("should produce different serialized values if the vector don't match", () => {
                fc.assert(
                    fc.property(vectorArbitrary, vectorArbitrary, (lineA, lineB) => {
                        fc.pre(!areVectorsEqual(lineA, lineB))

                        expect(serializeVector(lineA)).not.toBe(serializeVector(lineB))
                    })
                )
            })


it('should return true if two points are on the same stage', () => {
                fc.assert(
                    fc.property(endpointArbitrary, endpointArbitrary, addressArbitrary, (eid1, eid2, address) => {
                        fc.pre(endpointIdToStage(eid1) === endpointIdToStage(eid2))

                        expect(
                            isVectorPossible({ from: { eid: eid1, address }, to: { eid: eid2, address } })
                        ).toBeTruthy()
                    })
                )
            })


it('should return false if two points are not on the same stage', () => {
                fc.assert(
                    fc.property(endpointArbitrary, endpointArbitrary, addressArbitrary, (eid1, eid2, address) => {
                        fc.pre(endpointIdToStage(eid1) !== endpointIdToStage(eid2))

                        expect(
                            isVectorPossible({ from: { eid: eid1, address }, to: { eid: eid2, address } })
                        ).toBeFalsy()
                    })
                )
            })


it('should append eid to a value', () => {
            fc.assert(
                fc.property(endpointArbitrary, fc.dictionary(fc.string(), fc.anything()), (eid, value) => {
                    expect(withEid(eid)(value)).toEqual({ ...value, eid })
                })
            )
        })


it('should return an empty array if configuration function returns null, undefined or an empty array', async () => {
            const createSdk = jest.fn()
            const configureNode = jest.fn()
            const configureNodes = createConfigureNodes<OmniGraph>(configureNode)

            await fc.assert(
                fc.asyncProperty(nodesArbitrary, async (contracts) => {
                    createSdk.mockClear()
                    configureNode.mockClear()

                    // We mock the node configurator to only return empty values
                    configureNode.mockResolvedValueOnce(null).mockResolvedValueOnce(undefined).mockResolvedValue([])

                    const graph = { contracts, connections: [] }
                    await expect(configureNodes(graph, createSdk)).resolves.toEqual([])

                    expect(createSdk).toHaveBeenCalledTimes(contracts.length)
                    expect(configureNode).toHaveBeenCalledTimes(contracts.length)
                })
            )
        })


it('should create an SDK and execute configurator for every node', async () => {
            // Just some random SDK factory
            const createSdk = jest.fn().mockImplementation((point: OmniPoint) => ({ sdkFor: point }))
            // And a configuration function that returns something that we can identify in the result
            const configureNode = jest
                .fn()
                .mockImplementation((node: OmniNode, sdk: unknown) => ({ point: node.point, sdk }))
            const configureNodes = createConfigureNodes<OmniGraph>(configureNode)

            await fc.assert(
                fc.asyncProperty(nodesArbitrary, async (contracts) => {
                    createSdk.mockClear()
                    configureNode.mockClear()

                    const graph = { contracts, connections: [] }
                    const transactions = await configureNodes(graph, createSdk)

                    // First we check that the transactions match what our configuration function would return
                    expect(transactions).toHaveLength(contracts.length)
                    expect(transactions).toEqual(
                        contracts.map(({ point }) => ({
                            point,
                            sdk: {
                                sdkFor: point,
                            },
                        }))
                    )

                    // Then we check that the configurations have been called properly
                    expect(createSdk).toHaveBeenCalledTimes(contracts.length)
                    expect(configureNode).toHaveBeenCalledTimes(contracts.length)

                    for (const contract of contracts) {
                        expect(createSdk).toHaveBeenCalledWith(contract.point)
                        expect(configureNode).toHaveBeenCalledWith(
                            contract,
                            expect.objectContaining({ sdkFor: contract.point }),
                            graph,
                            createSdk
                        )
                    }
                })
            )
        })


it('should return an empty array if configuration function returns null, undefined or an empty array', async () => {
            const createSdk = jest.fn()
            const configureEdge = jest.fn()
            const configureEdges = createConfigureEdges<OmniGraph>(configureEdge)

            await fc.assert(
                fc.asyncProperty(edgesArbitrary, async (connections) => {
                    createSdk.mockClear()
                    configureEdge.mockClear()

                    // We mock the node configurator to only return empty values
                    configureEdge.mockResolvedValueOnce(null).mockResolvedValueOnce(undefined).mockResolvedValue([])

                    const graph = { contracts: [], connections }
                    await expect(configureEdges(graph, createSdk)).resolves.toEqual([])

                    expect(createSdk).toHaveBeenCalledTimes(connections.length)
                    expect(configureEdge).toHaveBeenCalledTimes(connections.length)
                })
            )
        })


it('should create an SDK and execute configurator for every connection', async () => {
            // Just some random SDK factory
            const createSdk = jest.fn().mockImplementation((point: OmniPoint) => ({ sdkFor: point }))
            // And a configuration function that returns something that we can identify in the result
            const configureEdge = jest
                .fn()
                .mockImplementation((edge: OmniEdge, sdk: unknown) => ({ vector: edge.vector, sdk }))
            const configureEdges = createConfigureEdges<OmniGraph>(configureEdge)

            await fc.assert(
                fc.asyncProperty(edgesArbitrary, async (connections) => {
                    createSdk.mockClear()
                    configureEdge.mockClear()

                    const graph = { contracts: [], connections }
                    const transactions = await configureEdges(graph, createSdk)

                    // First we check that the transactions match what our configuration function would return
                    expect(transactions).toHaveLength(connections.length)
                    expect(transactions).toEqual(
                        connections.map(({ vector }) => ({
                            vector,
                            sdk: {
                                sdkFor: vector.from,
                            },
                        }))
                    )

                    // Then we check that the configurations have been called properly
                    expect(createSdk).toHaveBeenCalledTimes(connections.length)
                    expect(configureEdge).toHaveBeenCalledTimes(connections.length)

                    for (const contract of connections) {
                        expect(createSdk).toHaveBeenCalledWith(contract.vector.from)
                        expect(configureEdge).toHaveBeenCalledWith(
                            contract,
                            expect.objectContaining({ sdkFor: contract.vector.from }),
                            graph,
                            createSdk
                        )
                    }
                })
            )
        })


it('should return a configurator that does nothing with no configurators', async () => {
            const createSdk = jest.fn()
            const emptyConfigurator = createConfigureMultiple()

            await fc.assert(
                fc.asyncProperty(graphArbitrary, async (graph) => {
                    expect(createSdk).not.toHaveBeenCalled()

                    await expect(emptyConfigurator(graph, createSdk)).resolves.toEqual([])
                })
            )
        })


it('should call all configurators', async () => {
                const createSdk = jest.fn()

                await fc.assert(
                    fc.asyncProperty(
                        graphArbitrary,
                        fc.array(transactionArbitrary),
                        async (graph, transactionGroups) => {
                            createSdk.mockClear()

                            // We create a configurator for every group of transactions
                            const configurators = transactionGroups.map((transactions) =>
                                jest.fn().mockResolvedValue(transactions)
                            )

                            // Now we execute these configurators
                            const multiConfigurator = createConfigureMultiple(...configurators)

                            // And expect to get all the transactions back
                            await expect(multiConfigurator(graph, createSdk)).resolves.toEqual(transactionGroups.flat())

                            // We also check that every configurator has been called
                            for (const configurator of configurators) {
                                expect(configurator).toHaveBeenCalledOnce()
                            }
                        }
                    )
                )
            })


it('should work without contract name innit', () => {
            fc.assert(
                fc.property(pointArbitrary, (point) => {
                    fc.pre(!point.contractName)

                    expect(formatOmniPoint(point)).toBe(`[${point.address} @ ${formatEid(point.eid)}]`)
                })
            )
        })


it('should work with contract name innit', () => {
            fc.assert(
                fc.property(pointArbitrary, (point) => {
                    fc.pre(!!point.contractName)

                    expect(formatOmniPoint(point)).toBe(
                        `[${point.address} (${point.contractName}) @ ${formatEid(point.eid)}]`
                    )
                })
            )
        })


it('should just work innit', () => {
            fc.assert(
                fc.property(vectorArbitrary, (vector) => {
                    expect(formatOmniVector(vector)).toBe(
                        `${formatOmniPoint(vector.from)} → ${formatOmniPoint(vector.to)}`
                    )
                })
            )
        })


it('should add a single node', () => {
                fc.assert(
                    fc.property(nodeArbitrary, (node) => {
                        const builder = new OmniGraphBuilder()

                        builder.addNodes(node)
                        expect(builder.nodes).toEqual([node])
                    })
                )
            })


it('should not add a duplicate node', () => {
                fc.assert(
                    fc.property(nodeArbitrary, (node) => {
                        const builder = new OmniGraphBuilder()

                        builder.addNodes(node, node)
                        expect(builder.nodes).toEqual([node])
                    })
                )
            })


it('should overwrite a node if the points are equal', () => {
                fc.assert(
                    fc.property(pointArbitrary, nodeConfigArbitrary, nodeConfigArbitrary, (point, configA, configB) => {
                        const builder = new OmniGraphBuilder()

                        const nodeA = { point, config: configA }
                        const nodeB = { point, config: configB }

                        builder.addNodes(nodeA, nodeB)
                        expect(builder.nodes).toEqual([nodeB])
                    })
                )
            })


it('should not do anything when there are no nodes', () => {
                fc.assert(
                    fc.property(nodeArbitrary, (node) => {
                        const builder = new OmniGraphBuilder()

                        builder.removeNodeAt(node.point)
                        expect(builder.nodes).toEqual([])
                    })
                )
            })


it('should return self', () => {
                fc.assert(
                    fc.property(nodeArbitrary, (node) => {
                        const builder = new OmniGraphBuilder()

                        expect(builder.removeNodeAt(node.point)).toBe(builder)
                    })
                )
            })


it('should remove a node at a specified point', () => {
                fc.assert(
                    fc.property(nodeArbitrary, (node) => {
                        const builder = new OmniGraphBuilder()

                        builder.addNodes(node)
                        builder.removeNodeAt(node.point)
                        expect(builder.nodes).toEqual([])
                    })
                )
            })


it('should not remove nodes at different points', () => {
                fc.assert(
                    fc.property(nodeArbitrary, nodeArbitrary, (nodeA, nodeB) => {
                        fc.pre(!arePointsEqual(nodeA.point, nodeB.point))

                        const builder = new OmniGraphBuilder()

                        builder.addNodes(nodeA, nodeB)
                        builder.removeNodeAt(nodeA.point)
                        expect(builder.nodes).toEqual([nodeB])
                    })
                )
            })


it('should remove all edges starting at the node', () => {
                fc.assert(
                    fc.property(
                        nodeArbitrary,
                        nodeArbitrary,
                        nodeArbitrary,
                        edgeConfigArbitrary,
                        (nodeA, nodeB, nodeC, edgeConfig) => {
                            fc.pre(!arePointsEqual(nodeA.point, nodeB.point))
                            fc.pre(!arePointsEqual(nodeA.point, nodeC.point))
                            fc.pre(!arePointsEqual(nodeB.point, nodeC.point))

                            const edgeAB = { vector: { from: nodeA.point, to: nodeB.point }, config: edgeConfig }
                            const edgeAC = { vector: { from: nodeA.point, to: nodeC.point }, config: edgeConfig }
                            const edgeBA = { vector: { from: nodeB.point, to: nodeA.point }, config: edgeConfig }
                            const edgeBC = { vector: { from: nodeB.point, to: nodeC.point }, config: edgeConfig }
                            const edgeCA = { vector: { from: nodeC.point, to: nodeA.point }, config: edgeConfig }
                            const edgeCB = { vector: { from: nodeC.point, to: nodeB.point }, config: edgeConfig }

                            fc.pre(isVectorPossible(edgeAB.vector))
                            fc.pre(isVectorPossible(edgeAC.vector))
                            fc.pre(isVectorPossible(edgeBA.vector))
                            fc.pre(isVectorPossible(edgeBC.vector))
                            fc.pre(isVectorPossible(edgeCA.vector))
                            fc.pre(isVectorPossible(edgeCB.vector))

                            const builder = new OmniGraphBuilder()

                            builder
                                .addNodes(nodeA, nodeB, nodeC)
                                .addEdges(edgeAB, edgeAC, edgeBA, edgeBC, edgeCA, edgeCB)
                                .removeNodeAt(nodeA.point)
                            expect(builder.edges).toEqual([edgeBA, edgeBC, edgeCA, edgeCB])
                        }
                    )
                )
            })


it('should fail if from is not in the graph', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes(
                                { point: edge.vector.from, config: nodeConfig },
                                { point: edge.vector.to, config: nodeConfig }
                            )
                            .removeNodeAt(edge.vector.from)

                        expect(() => builder.addEdges(edge)).toThrow()
                    })
                )
            })


it('should fail if vector is not possible', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(!isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes(
                                { point: edge.vector.from, config: nodeConfig },
                                { point: edge.vector.to, config: nodeConfig }
                            )
                            .removeNodeAt(edge.vector.from)

                        expect(() => builder.addEdges(edge)).toThrow()
                    })
                )
            })


it('should not fail if to is not in the graph', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes(
                                { point: edge.vector.from, config: nodeConfig },
                                { point: edge.vector.to, config: nodeConfig }
                            )
                            .removeNodeAt(edge.vector.to)
                            .addEdges(edge)

                        expect(builder.edges).toEqual([edge])
                    })
                )
            })


it('should add a single edge', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes({ point: edge.vector.from, config: nodeConfig })
                            .addNodes({ point: edge.vector.to, config: nodeConfig })
                            .addEdges(edge)
                        expect(builder.edges).toEqual([edge])
                    })
                )
            })


it('should not add a duplicate edge', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes({ point: edge.vector.from, config: nodeConfig })
                            .addNodes({ point: edge.vector.to, config: nodeConfig })
                            .addEdges(edge, edge)
                        expect(builder.edges).toEqual([edge])
                    })
                )
            })


it('should overwrite an edge if the points are equal', () => {
                fc.assert(
                    fc.property(
                        vectorArbitrary,
                        edgeConfigArbitrary,
                        edgeConfigArbitrary,
                        nodeConfigArbitrary,
                        (vector, configA, configB, nodeConfig) => {
                            fc.pre(isVectorPossible(vector))

                            const builder = new OmniGraphBuilder()

                            const edgeA = { vector, config: configA }
                            const edgeB = { vector, config: configB }

                            builder
                                .addNodes({ point: vector.from, config: nodeConfig })
                                .addNodes({ point: vector.to, config: nodeConfig })
                                .addEdges(edgeA, edgeB)
                            expect(builder.edges).toEqual([edgeB])
                        }
                    )
                )
            })


it('should not do anything when there are no edges', () => {
                fc.assert(
                    fc.property(edgeArbitrary, (edge) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder.removeEdgeAt(edge.vector)
                        expect(builder.edges).toEqual([])
                    })
                )
            })


it('should return self', () => {
                fc.assert(
                    fc.property(edgeArbitrary, (edge) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        expect(builder.removeEdgeAt(edge.vector)).toBe(builder)
                    })
                )
            })


it('should remove a edge at a specified vector', () => {
                fc.assert(
                    fc.property(edgeArbitrary, nodeConfigArbitrary, (edge, nodeConfig) => {
                        fc.pre(isVectorPossible(edge.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes({ point: edge.vector.from, config: nodeConfig })
                            .addNodes({ point: edge.vector.to, config: nodeConfig })
                            .addEdges(edge)

                        builder.removeEdgeAt(edge.vector)
                        expect(builder.edges).toEqual([])
                    })
                )
            })


it('should not remove edges at different vectors', () => {
                fc.assert(
                    fc.property(edgeArbitrary, edgeArbitrary, nodeConfigArbitrary, (edgeA, edgeB, nodeConfig) => {
                        fc.pre(isVectorPossible(edgeA.vector))
                        fc.pre(isVectorPossible(edgeB.vector))
                        fc.pre(!areVectorsEqual(edgeA.vector, edgeB.vector))

                        const builder = new OmniGraphBuilder()

                        builder
                            .addNodes({ point: edgeA.vector.from, config: nodeConfig })
                            .addNodes({ point: edgeA.vector.to, config: nodeConfig })
                            .addNodes({ point: edgeB.vector.from, config: nodeConfig })
                            .addNodes({ point: edgeB.vector.to, config: nodeConfig })
                            .addEdges(edgeA, edgeB)
                        builder.removeEdgeAt(edgeA.vector)
                        expect(builder.edges).toEqual([edgeB])
                    })
                )
            })


it('should return undefined when there are no nodes', () => {
                const builder = new OmniGraphBuilder()

                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(builder.getNodeAt(point)).toBeUndefined()
                    })
                )
            })


it('should return undefined when there are no nodes at a specified point', () => {
                fc.assert(
                    fc.property(nodesArbitrary, (nodes) => {
                        const node = nodes.at(-1)
                        fc.pre(node != null)

                        const builder = new OmniGraphBuilder()

                        builder.addNodes(...nodes)
                        builder.removeNodeAt(node!.point)
                        expect(builder.getNodeAt(node!.point)).toBeUndefined()
                    })
                )
            })


it('should return node when there is a node at a specified point', () => {
                fc.assert(
                    fc.property(nodesArbitrary, (nodes) => {
                        const node = nodes.at(-1)
                        fc.pre(node != null)

                        const builder = new OmniGraphBuilder()

                        builder.addNodes(...nodes)
                        expect(builder.getNodeAt(node!.point)).toBe(node)
                    })
                )
            })


it('should return undefined when there are no edges', () => {
                const builder = new OmniGraphBuilder()

                fc.assert(
                    fc.property(vectorArbitrary, (vector) => {
                        expect(builder.getEdgeAt(vector)).toBeUndefined()
                    })
                )
            })


it('should return undefined when there are no edges at a specified vector', () => {
                fc.assert(
                    fc.property(edgesArbitrary, nodeConfigArbitrary, (edges, nodeConfig) => {
                        const edge = edges.at(-1)
                        fc.pre(edge != null)
                        fc.pre(edges.map((e) => e.vector).every(isVectorPossible))

                        const builder = new OmniGraphBuilder()
                        const nodes = edges.flatMap(({ vector: { from, to } }) => [
                            { point: from, config: nodeConfig },
                            { point: to, config: nodeConfig },
                        ])

                        builder
                            .addNodes(...nodes)
                            .addEdges(...edges)
                            .removeEdgeAt(edge!.vector)
                        expect(builder.getEdgeAt(edge!.vector)).toBeUndefined()
                    })
                )
            })


it('should return edge when there is a edge at a specified vector', () => {
                fc.assert(
                    fc.property(edgesArbitrary, nodeConfigArbitrary, (edges, nodeConfig) => {
                        const edge = edges.at(-1)
                        fc.pre(edge != null)
                        fc.pre(edges.map((e) => e.vector).every(isVectorPossible))

                        const builder = new OmniGraphBuilder()
                        const nodes = edges.flatMap(({ vector: { from, to } }) => [
                            { point: from, config: nodeConfig },
                            { point: to, config: nodeConfig },
                        ])

                        builder.addNodes(...nodes).addEdges(...edges)
                        expect(builder.getEdgeAt(edge!.vector)).toBe(edge)
                    })
                )
            })


it('should return an empty array when there are no edges', () => {
                const builder = new OmniGraphBuilder()

                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(builder.getEdgesFrom(point)).toEqual([])
                    })
                )
            })


it('should return all edges that originate at a specific point', () => {
                fc.assert(
                    fc.property(edgesArbitrary, nodeConfigArbitrary, (edges, nodeConfig) => {
                        const edge = edges.at(-1)
                        fc.pre(edge != null)
                        fc.pre(edges.map((e) => e.vector).every(isVectorPossible))

                        const builder = new OmniGraphBuilder()
                        const nodes = edges.flatMap(({ vector: { from, to } }) => [
                            { point: from, config: nodeConfig },
                            { point: to, config: nodeConfig },
                        ])

                        builder.addNodes(...nodes).addEdges(...edges)

                        const edgesFrom = builder.edges.filter(({ vector }) =>
                            arePointsEqual(vector.from, edge!.vector.from)
                        )
                        expect(builder.getEdgesFrom(edge!.vector.from)).toEqual(edgesFrom)
                    })
                )
            })


it('should return an empty array when there are no edges', () => {
                const builder = new OmniGraphBuilder()

                fc.assert(
                    fc.property(pointArbitrary, (point) => {
                        expect(builder.getEdgesTo(point)).toEqual([])
                    })
                )
            })