Skip to content
This repository has been archived by the owner on Dec 27, 2022. It is now read-only.

Implement custom RPC Provider with Fallback #640

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
11 changes: 6 additions & 5 deletions modules/contracts/src.ts/constants.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { HDNode } from "@ethersproject/hdnode";
import { Wallet } from "@ethersproject/wallet";
import { JsonRpcProvider } from "@ethersproject/providers";
import { network, ethers }from "hardhat";
import { ChainRpcProvider } from "@connext/vector-types";
import { network, ethers } from "hardhat";
import pino from "pino";

// Get defaults from env
const chainProviders = JSON.parse(process.env.CHAIN_PROVIDERS ?? "{}");
const chainId = Object.keys(chainProviders)[0];
const url = Object.values(chainProviders)[0];
const urls = Object.values(chainProviders)[0];
const mnemonic = process.env.SUGAR_DADDY ?? "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat";

export const defaultLogLevel = process.env.LOG_LEVEL || "info";
export const logger = pino({ level: defaultLogLevel });

export const networkName = network.name;

export const provider = url
? new JsonRpcProvider(url as string, parseInt(chainId))
: ethers.provider as JsonRpcProvider;
export const provider = urls
? new ChainRpcProvider(parseInt(chainId), (urls as string).split(","))
: new ChainRpcProvider(parseInt(chainId), [ethers.provider as JsonRpcProvider]);

const hdNode = HDNode.fromMnemonic(mnemonic).derivePath("m/44'/60'/0'/0");

Expand Down
10 changes: 5 additions & 5 deletions modules/contracts/src.ts/services/ethReader.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainError, FullChannelState, Result } from "@connext/vector-types";
import { ChainError, FullChannelState, Result, ChainRpcProvider } from "@connext/vector-types";
import { createTestChannelState, expect, getTestLoggers, mkHash } from "@connext/vector-utils";
import { JsonRpcProvider, TransactionReceipt } from "@ethersproject/providers";
import { TransactionReceipt } from "@ethersproject/providers";
import { AddressZero, One, Zero } from "@ethersproject/constants";
import { parseUnits } from "@ethersproject/units";
import { restore, reset, createStubInstance, SinonStubbedInstance } from "sinon";
Expand All @@ -9,8 +9,8 @@ import { EthereumChainReader, MIN_GAS_PRICE, BUMP_GAS_PRICE } from "./ethReader"

let ethReader: EthereumChainReader;
let channelState: FullChannelState;
let provider1337: SinonStubbedInstance<JsonRpcProvider>;
let provider1338: SinonStubbedInstance<JsonRpcProvider>;
let provider1337: SinonStubbedInstance<ChainRpcProvider>;
let provider1338: SinonStubbedInstance<ChainRpcProvider>;

const assertResult = (result: Result<any>, isError: boolean, unwrappedVal?: any) => {
if (isError) {
Expand Down Expand Up @@ -46,7 +46,7 @@ describe("ethReader", () => {
beforeEach(() => {
// eth service deps

const _provider = createStubInstance(JsonRpcProvider);
const _provider = createStubInstance(ChainRpcProvider);
_provider.getTransaction.resolves(_txResponse);
provider1337 = _provider;
provider1338 = _provider;
Expand Down
7 changes: 4 additions & 3 deletions modules/contracts/src.ts/services/ethReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ import {
CoreChannelState,
CoreTransferState,
TransferDispute,
ChainRpcProvider,
} from "@connext/vector-types";
import axios from "axios";
import { encodeBalance, encodeTransferResolver, encodeTransferState } from "@connext/vector-utils";
import { BigNumber } from "@ethersproject/bignumber";
import { parseUnits } from "@ethersproject/units";
import { AddressZero, HashZero } from "@ethersproject/constants";
import { Contract } from "@ethersproject/contracts";
import { JsonRpcProvider, TransactionRequest } from "@ethersproject/providers";
import { TransactionRequest } from "@ethersproject/providers";
import pino from "pino";

import { ChannelFactory, ChannelMastercopy, TransferDefinition, TransferRegistry, VectorChannel } from "../artifacts";
Expand All @@ -59,14 +60,14 @@ export class EthereumChainReader implements IVectorChainReader {
};
private contracts: Map<string, Contract> = new Map();
constructor(
public readonly chainProviders: { [chainId: string]: JsonRpcProvider },
public readonly chainProviders: { [chainId: string]: ChainRpcProvider },
public readonly log: pino.BaseLogger,
) {}

getChainProviders(): Result<ChainProviders, ChainError> {
const ret: ChainProviders = {};
Object.entries(this.chainProviders).forEach(([name, value]) => {
ret[parseInt(name)] = value.connection.url;
ret[parseInt(name)] = value.providerUrls;
});
return Result.ok(ret);
}
Expand Down
9 changes: 5 additions & 4 deletions modules/contracts/src.ts/services/ethService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Result,
StoredTransaction,
TransactionReason,
ChainRpcProvider,
} from "@connext/vector-types";
import {
ChannelSigner,
Expand All @@ -19,7 +20,7 @@ import {
mkHash,
} from "@connext/vector-utils";
import { AddressZero, One, Zero } from "@ethersproject/constants";
import { JsonRpcProvider, TransactionReceipt, TransactionResponse } from "@ethersproject/providers";
import { TransactionReceipt, TransactionResponse } from "@ethersproject/providers";
import { BigNumber } from "ethers";
import { parseUnits } from "ethers/lib/utils";
import { restore, reset, createStubInstance, SinonStubbedInstance, stub, SinonStub } from "sinon";
Expand All @@ -30,8 +31,8 @@ import { BIG_GAS_PRICE, EthereumChainService } from "./ethService";
let storeMock: SinonStubbedInstance<IChainServiceStore>;
let signer: SinonStubbedInstance<IChannelSigner>;
let ethService: EthereumChainService;
let provider1337: SinonStubbedInstance<JsonRpcProvider>;
let provider1338: SinonStubbedInstance<JsonRpcProvider>;
let provider1337: SinonStubbedInstance<ChainRpcProvider>;
let provider1338: SinonStubbedInstance<ChainRpcProvider>;

let sendTxWithRetriesMock: SinonStub;
let approveMock: SinonStub;
Expand Down Expand Up @@ -96,7 +97,7 @@ describe("ethService unit test", () => {
signer.connect.returns(signer as any);
(signer as any)._isSigner = true;

const _provider = createStubInstance(JsonRpcProvider);
const _provider = createStubInstance(ChainRpcProvider);
_provider.getTransaction.resolves(txResponse);
provider1337 = _provider;
provider1338 = _provider;
Expand Down
9 changes: 5 additions & 4 deletions modules/contracts/src.ts/services/ethService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
StringifiedTransactionResponse,
getConfirmationsForChain,
StoredTransaction,
ChainRpcProvider,
} from "@connext/vector-types";
import {
delay,
Expand All @@ -29,7 +30,7 @@ import {
import { Signer } from "@ethersproject/abstract-signer";
import { BigNumber } from "@ethersproject/bignumber";
import { Contract } from "@ethersproject/contracts";
import { JsonRpcProvider, TransactionReceipt, TransactionResponse } from "@ethersproject/providers";
import { TransactionReceipt, TransactionResponse } from "@ethersproject/providers";
import { Wallet } from "@ethersproject/wallet";
import { BaseLogger } from "pino";
import PriorityQueue from "p-queue";
Expand All @@ -54,7 +55,7 @@ export const BIG_GAS_PRICE = parseUnits("1500", "gwei");

// TODO: Deprecate. Note that this is used in autoRebalance.ts.
export const waitForTransaction = async (
provider: JsonRpcProvider,
provider: ChainRpcProvider,
transactionHash: string,
confirmations?: number,
timeout?: number,
Expand Down Expand Up @@ -84,7 +85,7 @@ export class EthereumChainService extends EthereumChainReader implements IVector
};
constructor(
private readonly store: IChainServiceStore,
chainProviders: { [chainId: string]: JsonRpcProvider },
chainProviders: { [chainId: string]: ChainRpcProvider },
signer: string | Signer,
log: BaseLogger,
private readonly defaultRetries = 3,
Expand Down Expand Up @@ -528,7 +529,7 @@ export class EthereumChainService extends EthereumChainReader implements IVector
* the tx will be resubmitted at the same nonce.
*/
public async waitForConfirmation(chainId: number, responses: TransactionResponse[]): Promise<TransactionReceipt> {
const provider: JsonRpcProvider = this.chainProviders[chainId];
const provider = this.chainProviders[chainId];
if (!provider) {
throw new ChainError(ChainError.reasons.ProviderNotFound);
}
Expand Down
4 changes: 2 additions & 2 deletions modules/iframe-app/src/ConnextManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
EngineParams,
jsonifyError,
} from "@connext/vector-types";
import { ChannelSigner, constructRpcRequest, safeJsonParse } from "@connext/vector-utils";
import { ChannelSigner, constructRpcRequest, safeJsonParse, parseProviders } from "@connext/vector-utils";
import { entropyToMnemonic } from "@ethersproject/hdnode";
import { keccak256 } from "@ethersproject/keccak256";
import { toUtf8Bytes } from "@ethersproject/strings";
Expand Down Expand Up @@ -89,7 +89,7 @@ export default class ConnextManager {
this.browserNode = await BrowserNode.connect({
signer,
chainAddresses: chainAddresses ?? config.chainAddresses,
chainProviders,
chainProviders: parseProviders(chainProviders),
logger: pino(),
messagingUrl: _messagingUrl,
authUrl: _authUrl,
Expand Down
4 changes: 2 additions & 2 deletions modules/protocol/src/testing/constants.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { JsonRpcProvider } from "@ethersproject/providers";
import { ChainRpcProvider } from "@connext/vector-types";
import { Wallet } from "@ethersproject/wallet";

import { env } from "./env";

export const chainId = parseInt(Object.keys(env.chainProviders)[0]);
export const tokenAddress = env.chainAddresses[chainId]?.testTokenAddress ?? "";
export const provider = new JsonRpcProvider(env.chainProviders[chainId], chainId);
export const provider = new ChainRpcProvider(chainId, [env.chainProviders[chainId]]);

export const sugarDaddy = Wallet.fromMnemonic(env.sugarDaddyMnemonic).connect(provider);
export const rando = Wallet.createRandom().connect(provider);
Expand Down
6 changes: 4 additions & 2 deletions modules/router/src/services/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
jsonifyError,
IVectorChainReader,
DEFAULT_ROUTER_MAX_SAFE_PRICE_IMPACT,
ChainRpcProvider,
} from "@connext/vector-types";
import { JsonRpcProvider } from "@ethersproject/providers";
import { StableSwap } from "@connext/vector-contracts";
import { getAddress } from "@ethersproject/address";
import { BigNumber } from "@ethersproject/bignumber";
Expand Down Expand Up @@ -132,7 +132,9 @@ export const onSwapGivenIn = async (
logger: BaseLogger,
): Promise<Result<{ priceImpact: string; amountOut: BigNumber }, ConfigServiceError>> => {
const { stableAmmChainId, stableAmmAddress } = getConfig();
const stableAmmProvider: JsonRpcProvider = new JsonRpcProvider(getConfig().chainProviders[stableAmmChainId!]);
const stableAmmProvider: ChainRpcProvider = new ChainRpcProvider(
stableAmmChainId!, [getConfig().chainProviders[stableAmmChainId!]]
);

// if there's no swap, rate is 1:1
if (fromAssetId === toAssetId && fromChainId === toChainId) {
Expand Down
11 changes: 5 additions & 6 deletions modules/router/src/test/autoRebalance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { VectorChainReader } from "@connext/vector-contracts";
import { expect, getRandomBytes32, getTestLoggers, mkAddress, mkBytes32 } from "@connext/vector-utils";
import Sinon from "sinon";
import { AllowedSwap, Result } from "@connext/vector-types";
import { AllowedSwap, ChainRpcProvider, Result } from "@connext/vector-types";
import { Wallet } from "@ethersproject/wallet";
import { JsonRpcProvider } from "@ethersproject/providers";
import { BigNumber } from "@ethersproject/bignumber";
import { parseEther } from "@ethersproject/units";
import axios from "axios";
Expand All @@ -25,7 +24,7 @@ const { log } = getTestLoggers(testName, config.logLevel as any);
const setupForRebalance = (
mockAxios: Sinon.SinonStubbedInstance<any>,
wallet: Sinon.SinonStubbedInstance<Wallet>,
hydratedProviders: { [chainId: number]: Sinon.SinonStubbedInstance<JsonRpcProvider> },
hydratedProviders: { [chainId: number]: Sinon.SinonStubbedInstance<ChainRpcProvider> },
chainService: Sinon.SinonStubbedInstance<VectorChainReader>,
): {
transaction: {
Expand Down Expand Up @@ -112,7 +111,7 @@ describe(testName, () => {
describe("rebalanceIfNeeded", () => {
let wallet: Sinon.SinonStubbedInstance<Wallet>;
let chainService: Sinon.SinonStubbedInstance<VectorChainReader>;
let hydratedProviders: { [chainId: number]: Sinon.SinonStubbedInstance<JsonRpcProvider> };
let hydratedProviders: { [chainId: number]: Sinon.SinonStubbedInstance<ChainRpcProvider> };
let mockAxios: Sinon.SinonStubbedInstance<any>;
let mockConfirmation: Sinon.SinonStubbedInstance<any>;
let store: Sinon.SinonStubbedInstance<PrismaStore>;
Expand All @@ -132,8 +131,8 @@ describe(testName, () => {

chainService = Sinon.createStubInstance(VectorChainReader);
hydratedProviders = {
1337: Sinon.createStubInstance(JsonRpcProvider),
1338: Sinon.createStubInstance(JsonRpcProvider),
1337: Sinon.createStubInstance(ChainRpcProvider),
1338: Sinon.createStubInstance(ChainRpcProvider),
};
const parseBalanceStub = Sinon.stub(metrics, "getDecimals").resolves(18);
hydratedProviders[1337].getGasPrice.resolves(BigNumber.from(138));
Expand Down
Loading