Skip to content

Commit

Permalink
Merge pull request #38 from helix-bridge/xiaoch05-multi-rpcs
Browse files Browse the repository at this point in the history
support connect with multi rpc nodes
  • Loading branch information
xiaoch05 authored Sep 6, 2024
2 parents 5da1f76 + 4004e2e commit 3ad2cb0
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 68 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"@safe-global/safe-core-sdk-types": "^5.0.2",
"axios": "^1.2.3",
"dids": "^5.0.2",
"ethers": "^6.7.0",
"ethers": "^6.13.0",
"https-proxy-agent": "^7.0.5",
"key-did-provider-ed25519": "^4.0.2",
"key-did-resolver": "^4.0.0",
Expand Down
50 changes: 39 additions & 11 deletions src/base/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { abiSafe } from "../abi/abiSafe";
import { multicall3 } from "../abi/multicall3";
import { weth } from "../abi/weth";
import { GasPrice } from "../base/provider";
import { EthereumConnectedWallet } from "./wallet";
import { EthereumProvider, rpcCallIfError } from "./provider";

export const zeroAddress: string = "0x0000000000000000000000000000000000000000";
export const zeroTransferId: string =
Expand All @@ -28,25 +30,34 @@ export interface MulticallArgs {
export class EthereumContract {
protected contract: Contract;
public address: string;
public tryNextUrl: () => void;
constructor(
address: string,
abi: InterfaceAbi,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
) {
this.contract = new Contract(address, abi, signer);
this.contract = new Contract(address, abi, signer.SignerOrProvider);
this.address = address;
this.tryNextUrl = () => {
signer.tryNextUrl();
};
signer.registerUrlUpdateHandler(() => {
this.contract = new Contract(address, abi, signer.SignerOrProvider);
});
}

get interface() {
return this.contract.interface;
}

@rpcCallIfError
async getSoftTransferLimit(
relayer: string,
targetToken: string,
provider: ethers.Provider
signer: EthereumProvider
): Promise<SoftLimitAmount> {
// native token
const provider = signer.SignerOrProvider as ethers.Provider;
if (targetToken === zeroAddress) {
const balance =
((await provider.getBalance(relayer)) * BigInt(9)) / BigInt(10);
Expand All @@ -55,7 +66,7 @@ export class EthereumContract {
allowance: balance,
};
} else {
const targetTokenContract = new Erc20Contract(targetToken, provider);
const targetTokenContract = new Erc20Contract(targetToken, signer);
const balance = await targetTokenContract.balanceOf(relayer);
const allowance = await targetTokenContract.allowance(
relayer,
Expand All @@ -68,6 +79,7 @@ export class EthereumContract {
}
}

@rpcCallIfError
async call(
method: string,
args: any,
Expand All @@ -86,6 +98,7 @@ export class EthereumContract {
return await this.contract[method](...args, txConfig);
}

@rpcCallIfError
async staticCall(
method: string,
args: any,
Expand Down Expand Up @@ -120,28 +133,33 @@ export class EthereumContract {
export class Erc20Contract extends EthereumContract {
constructor(
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
) {
super(address, erc20, signer);
}

// view
@rpcCallIfError
async symbol(): Promise<string> {
return await this.contract.symbol();
}

@rpcCallIfError
async name(): Promise<string> {
return await this.contract.name();
}

@rpcCallIfError
async decimals(): Promise<number> {
return await this.contract.decimals();
}

@rpcCallIfError
async balanceOf(address: string): Promise<bigint> {
return await this.contract.balanceOf(address);
}

@rpcCallIfError
async allowance(owner: string, spender: string): Promise<bigint> {
return await this.contract.allowance(owner, spender);
}
Expand All @@ -152,7 +170,7 @@ export class Erc20Contract extends EthereumContract {
amount: bigint,
gas: GasPrice
): Promise<TransactionResponse> {
return this.call("approve", [address, amount], gas, null, null, null);
return await this.call("approve", [address, amount], gas, null, null, null);
}

approveRawData(spender: string, amount: bigint): string {
Expand Down Expand Up @@ -206,7 +224,7 @@ export interface RelayRawData {
export class SafeContract extends EthereumContract {
constructor(
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
) {
super(address, abiSafe, signer);
}
Expand Down Expand Up @@ -275,7 +293,7 @@ export class LnBridgeContract extends EthereumContract {
private bridgeType: string;
constructor(
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider,
signer: EthereumConnectedWallet | EthereumProvider,
bridgeType: string
) {
if (bridgeType === "lnv2-default") {
Expand All @@ -299,6 +317,7 @@ export class LnBridgeContract extends EthereumContract {
return ethers.keccak256(encode);
}

@rpcCallIfError
async getLnProviderInfo(
remoteChainId: number,
relayer: string,
Expand Down Expand Up @@ -383,11 +402,13 @@ export class LnBridgeContract extends EthereumContract {
}
}

@rpcCallIfError
async transferIdExist(transferId: string): Promise<[boolean, any]> {
const lockInfo = await this.contract.lockInfos(transferId);
return [lockInfo.timestamp > 0, lockInfo];
}

@rpcCallIfError
async transferHasFilled(transferId: string): Promise<boolean> {
const fillInfo = await this.contract.fillTransfers(transferId);
if (this.bridgeType === "lnv2-default") {
Expand All @@ -397,6 +418,7 @@ export class LnBridgeContract extends EthereumContract {
}
}

@rpcCallIfError
async fillTransfers(transferId: string): Promise<any> {
return await this.contract.fillTransfers(transferId);
}
Expand Down Expand Up @@ -493,7 +515,7 @@ export class LnBridgeContract extends EthereumContract {
export class Lnv3BridgeContract extends EthereumContract {
constructor(
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
) {
super(address, lnv3Bridge, signer);
}
Expand Down Expand Up @@ -531,6 +553,7 @@ export class Lnv3BridgeContract extends EthereumContract {
return ethers.keccak256(encode);
}

@rpcCallIfError
async getLnProviderInfo(
remoteChainId: number,
relayer: string,
Expand All @@ -551,6 +574,7 @@ export class Lnv3BridgeContract extends EthereumContract {
};
}

@rpcCallIfError
async getLnProviderPenalty(
relayer: string,
sourceToken: string
Expand All @@ -559,6 +583,7 @@ export class Lnv3BridgeContract extends EthereumContract {
return await this.contract.penaltyReserves(providerStateKey);
}

@rpcCallIfError
async getTokenBasePenalty(
remoteChainId: number,
sourceToken: string,
Expand Down Expand Up @@ -696,16 +721,19 @@ export class Lnv3BridgeContract extends EthereumContract {
]);
}

@rpcCallIfError
async transferIdExist(transferId: string): Promise<[boolean, any]> {
const lockInfo = await this.contract.lockInfos(transferId);
return [lockInfo.status == LNV3_STATUS_LOCKED, lockInfo];
}

@rpcCallIfError
async transferHasFilled(transferId: string): Promise<boolean> {
const fillInfo = await this.contract.fillTransfers(transferId);
return fillInfo.timestamp > 0;
}

@rpcCallIfError
async fillTransfers(transferId: string): Promise<any> {
return await this.contract.fillTransfers(transferId);
}
Expand Down Expand Up @@ -817,7 +845,7 @@ export class Lnv3BridgeContract extends EthereumContract {
export class WETHContract extends EthereumContract {
constructor(
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
) {
super(address, weth, signer);
}
Expand All @@ -834,7 +862,7 @@ export class WETHContract extends EthereumContract {
export class MulticallContract extends EthereumContract {
constructor(
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
) {
super(address, multicall3, signer);
}
Expand Down
8 changes: 5 additions & 3 deletions src/base/messager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Wallet, HDNodeWallet, ethers, AbiCoder } from "ethers";
import { EthereumContract } from "./contract";
import { layerzeroMessager } from "../abi/layerzeroMessager";
import { msgportMessager } from "../abi/msgportMessager";
import { EthereumConnectedWallet } from "./wallet";
import { EthereumProvider } from "./provider";

export interface MessageParams {
fee: bigint;
Expand Down Expand Up @@ -31,7 +33,7 @@ export class MessagePortMessager extends Messager {

constructor(
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
) {
super(address, msgportMessager, signer);
}
Expand Down Expand Up @@ -69,7 +71,7 @@ export class MessagePortMessager extends Messager {
export class LayerzeroMessager extends Messager {
constructor(
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
) {
super(address, layerzeroMessager, signer);
}
Expand Down Expand Up @@ -104,7 +106,7 @@ export class LayerzeroMessager extends Messager {
export function messagerInstance(
channel: string,
address: string,
signer: Wallet | HDNodeWallet | ethers.Provider
signer: EthereumConnectedWallet | EthereumProvider
): Messager {
if (channel === "layerzero") {
return new LayerzeroMessager(address, signer);
Expand Down
55 changes: 53 additions & 2 deletions src/base/provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ethers } from "ethers";
import { Logger } from "@nestjs/common";
import { Wallet, HDNodeWallet, ethers } from "ethers";
import { GWei } from "./bignumber";

export interface EIP1559Fee {
Expand Down Expand Up @@ -38,21 +39,70 @@ export function scaleBigger(
}
}

export function rpcCallIfError(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const method = descriptor.value;
descriptor.value = async function (...args: any[]) {
try {
return await method.apply(this, args);
} catch (err) {
this.tryNextUrl();
throw err;
}
};
}

export class EthereumProvider {
public provider: ethers.JsonRpcProvider;
public onUrlUpdatedHandlers: (() => void)[] = [];
public urls: string[];
public urlIndex: number = 0;
private readonly logger = new Logger("provider");
private updateTimestamp: number = 0;

constructor(url: string) {
constructor(urls: string[]) {
this.urls = urls;
this.provider = new ethers.JsonRpcProvider(urls[0]);
}

tryNextUrl() {
const now = Date.now();
if (this.urls.length <= 1 || this.updateTimestamp + 60000 > now) {
return;
}
this.updateTimestamp = now;
this.urlIndex += 1;
const url = this.urls[this.urlIndex % this.urls.length];
this.logger.log(`try to use next url ${url}`);
this.provider.destroy();
this.provider = new ethers.JsonRpcProvider(url);
for (const handler of this.onUrlUpdatedHandlers) {
handler();
}
}

registerUrlUpdateHandler(handler: () => void) {
this.onUrlUpdatedHandlers.push(handler);
}

get SignerOrProvider(): Wallet | HDNodeWallet | ethers.Provider {
return this.provider;
}

@rpcCallIfError
async currentBlocknumber() {
return await this.provider.getBlockNumber();
}

@rpcCallIfError
async balanceOf(address: string): Promise<bigint> {
return await this.provider.getBalance(address);
}

@rpcCallIfError
async feeData(
scale: number,
notSupport1559: boolean = false
Expand Down Expand Up @@ -85,6 +135,7 @@ export class EthereumProvider {
}
}

@rpcCallIfError
async checkPendingTransaction(hash: string): Promise<TransactionInfo> | null {
const transaction = await this.provider.getTransaction(hash);
if (!transaction) {
Expand Down
Loading

0 comments on commit 3ad2cb0

Please sign in to comment.