Skip to content

Commit

Permalink
refactor: remove PythContractFactory class
Browse files Browse the repository at this point in the history
  • Loading branch information
ali-bahjati committed Aug 21, 2024
1 parent 0f8a1f4 commit e46c461
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 167 deletions.
25 changes: 15 additions & 10 deletions apps/price_pusher/src/evm/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { Controller } from "../controller";
import { EvmPriceListener, EvmPricePusher, PythContractFactory } from "./evm";
import { getCustomGasStation } from "./custom-gas-station";
import pino from "pino";
import { createClient } from "./super-wallet";
import { createPythContract } from "./pyth-contract";
import { isWsEndpoint } from "../utils";

export default {
command: "evm",
Expand Down Expand Up @@ -121,21 +124,22 @@ export default {
logger.child({ module: "PythPriceListener" })
);

const pythContractFactory = await PythContractFactory.create(
endpoint,
mnemonic,
pythContractAddress
);
const client = await createClient(endpoint, mnemonic);
const pythContract = createPythContract(client, pythContractAddress);

logger.info(
`Pushing updates from wallet address: ${
pythContractFactory.getAccount().address
}`
`Pushing updates from wallet address: ${client.account.address}`
);

// It is possible to watch the events in the non-ws endpoints, either by getFilter
// or by getLogs, but it is very expensive and our polling mechanism does it
// in a more efficient way. So we only do it with ws endpoints.
const watchEvents = isWsEndpoint(endpoint);

const evmListener = new EvmPriceListener(
pythContractFactory,
pythContract,
priceItems,
watchEvents,
logger.child({ module: "EvmPriceListener" }),
{
pollingFrequency,
Expand All @@ -149,7 +153,8 @@ export default {
);
const evmPusher = new EvmPricePusher(
priceServiceConnection,
pythContractFactory,
client,
pythContract,
logger.child({ module: "EvmPricePusher" }),
overrideGasPriceMultiplier,
overrideGasPriceMultiplierCap,
Expand Down
168 changes: 11 additions & 157 deletions apps/price_pusher/src/evm/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
} from "../utils";
import { PythAbi } from "./pyth-abi";
import { Logger } from "pino";
import { isWsEndpoint } from "../utils";
import {
PriceServiceConnection,
HexString,
Expand All @@ -21,94 +20,38 @@ import {
import { CustomGasStation } from "./custom-gas-station";
import { PushAttempt } from "../common";
import {
PublicClient,
Transport,
WalletClient,
createPublicClient,
createWalletClient,
getContract,
defineChain,
http,
webSocket,
Address,
GetContractReturnType,
WatchContractEventOnLogsParameter,
TransactionExecutionError,
Account,
BaseError,
ContractFunctionRevertedError,
FeeCapTooLowError,
InternalRpcError,
InsufficientFundsError,
Chain,
publicActions,
Client,
RpcSchema,
WalletActions,
PublicActions,
} from "viem";

import { mnemonicToAccount } from "viem/accounts";
import * as chains from "viem/chains";

type PythContract = GetContractReturnType<
typeof PythAbi,
PublicClient | WalletClient
>;

export type SuperWalletClient<
transport extends Transport = Transport,
chain extends Chain | undefined = Chain,
account extends Account | undefined = Account
> = Client<
transport,
chain,
account,
RpcSchema,
PublicActions<transport, chain, account> & WalletActions<chain, account>
>;

const UNKNOWN_CHAIN_CONFIG = {
name: "Unknown",
nativeCurrency: {
name: "Unknown",
symbol: "Unknown",
decimals: 18,
},
rpcUrls: {
default: {
http: [],
},
},
};
import { PythContract } from "./pyth-contract";
import { SuperWalletClient } from "./super-wallet";

export class EvmPriceListener extends ChainPriceListener {
private pythContractFactory: PythContractFactory;
private pythContract: PythContract;
private logger: Logger;

constructor(
pythContractFactory: PythContractFactory,
private pythContract: PythContract,
priceItems: PriceItem[],
logger: Logger,
private watchEvents: boolean,
private logger: Logger,
config: {
pollingFrequency: DurationInSeconds;
}
) {
super(config.pollingFrequency, priceItems);

this.pythContractFactory = pythContractFactory;
this.pythContract = this.pythContractFactory.createPythContract();
this.pythContract = pythContract;
this.logger = logger;
}

// This method should be awaited on and once it finishes it has the latest value
// for the given price feeds (if they exist).
async start() {
// It is possible to watch the events in the non-ws endpoints, either by getFilter
// or by getLogs, but it is very expensive and our polling mechanism does it
// in a more efficient way.
if (this.pythContractFactory.hasWebsocketProvider()) {
if (this.watchEvents) {
this.logger.info("Watching target network pyth contract events...");
this.startWatching();
} else {
Expand Down Expand Up @@ -180,26 +123,20 @@ export class EvmPriceListener extends ChainPriceListener {
}

export class EvmPricePusher implements IPricePusher {
private customGasStation?: CustomGasStation;
private client: SuperWalletClient;
private pythContract: PythContract;
private pusherAddress: `0x${string}` | undefined;
private lastPushAttempt: PushAttempt | undefined;

constructor(
private connection: PriceServiceConnection,
pythContractFactory: PythContractFactory,
private client: SuperWalletClient,
private pythContract: PythContract,
private logger: Logger,
private overrideGasPriceMultiplier: number,
private overrideGasPriceMultiplierCap: number,
private updateFeeMultiplier: number,
private gasLimit?: number,
customGasStation?: CustomGasStation
) {
this.customGasStation = customGasStation;
this.pythContract = pythContractFactory.createPythContract();
this.client = pythContractFactory.createClient();
}
private customGasStation?: CustomGasStation
) {}

// The pubTimes are passed here to use the values that triggered the push.
// This is an optimization to avoid getting a newer value (as an update comes)
Expand Down Expand Up @@ -468,86 +405,3 @@ export class EvmPricePusher implements IPricePusher {
);
}
}

export class PythContractFactory {
private endpoint: string;
private mnemonic: string;
private pythContractAddress: Address;
private chainId: number;

private constructor(
endpoint: string,
mnemonic: string,
pythContractAddress: Address,
chainId: number
) {
this.endpoint = endpoint;
this.mnemonic = mnemonic;
this.pythContractAddress = pythContractAddress;
this.chainId = chainId;
}

static async create(
endpoint: string,
mnemonic: string,
pythContractAddress: Address
): Promise<PythContractFactory> {
const chainId = await createPublicClient({
transport: PythContractFactory.getTransport(endpoint),
}).getChainId();
return new PythContractFactory(
endpoint,
mnemonic,
pythContractAddress,
chainId
);
}

/**
* This method creates a web3 Pyth contract with payer (based on mnemonic).
*
* @returns Pyth contract
*/
createPythContract(): PythContract {
return getContract({
address: this.pythContractAddress,
abi: PythAbi,
client: this.createClient(),
});
}

hasWebsocketProvider(): boolean {
return isWsEndpoint(this.endpoint);
}

getAccount(): Account {
return mnemonicToAccount(this.mnemonic);
}

createClient(): SuperWalletClient {
return createWalletClient({
transport: PythContractFactory.getTransport(this.endpoint),
account: mnemonicToAccount(this.mnemonic),
chain: PythContractFactory.getChain(this.chainId),
}).extend(publicActions);
}

// Get the chain corresponding to the chainId. If the chain is not found, it will return
// an unknown chain which should work fine in most of the cases. We might need to update
// the viem package to support new chains if they don't work as expected with the unknown
// chain.
private static getChain(chainId: number): Chain {
return (
Object.values(chains).find((chain) => chain.id === chainId) ||
defineChain({ id: chainId, ...UNKNOWN_CHAIN_CONFIG })
);
}

private static getTransport(endpoint: string): Transport {
if (isWsEndpoint(endpoint)) {
return webSocket(endpoint);
} else {
return http(endpoint);
}
}
}
18 changes: 18 additions & 0 deletions apps/price_pusher/src/evm/pyth-contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getContract, Address, GetContractReturnType } from "viem";
import { PythAbi } from "./pyth-abi";
import { SuperWalletClient } from "./super-wallet";

export type PythContract = GetContractReturnType<
typeof PythAbi,
SuperWalletClient
>;

export const createPythContract = (
client: SuperWalletClient,
address: Address
): PythContract =>
getContract({
client,
abi: PythAbi,
address,
});
71 changes: 71 additions & 0 deletions apps/price_pusher/src/evm/super-wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {
createPublicClient,
createWalletClient,
defineChain,
http,
webSocket,
Account,
Chain,
publicActions,
Client,
RpcSchema,
WalletActions,
PublicActions,
WebSocketTransport,
HttpTransport,
Transport,
} from "viem";
import { mnemonicToAccount } from "viem/accounts";
import * as chains from "viem/chains";
import { isWsEndpoint } from "../utils";

const UNKNOWN_CHAIN_CONFIG = {
name: "Unknown",
nativeCurrency: {
name: "Unknown",
symbol: "Unknown",
decimals: 18,
},
rpcUrls: {
default: {
http: [],
},
},
};

export type SuperWalletClient = Client<
Transport,
Chain,
Account,
RpcSchema,
PublicActions<Transport, Chain, Account> & WalletActions<Chain, Account>
>;

// Get the transport based on the endpoint
const getTransport = (endpoint: string): WebSocketTransport | HttpTransport =>
isWsEndpoint(endpoint) ? webSocket(endpoint) : http(endpoint);

// Get the chain corresponding to the chainId. If the chain is not found, it will return
// an unknown chain which should work fine in most of the cases. We might need to update
// the viem package to support new chains if they don't work as expected with the unknown
// chain.
const getChainById = (chainId: number): Chain =>
Object.values(chains).find((chain) => chain.id === chainId) ||
defineChain({ id: chainId, ...UNKNOWN_CHAIN_CONFIG });

export const createClient = async (
endpoint: string,
mnemonic: string
): Promise<SuperWalletClient> => {
const transport = getTransport(endpoint);

const chainId = await createPublicClient({
transport,
}).getChainId();

return createWalletClient({
transport,
account: mnemonicToAccount(mnemonic),
chain: getChainById(chainId),
}).extend(publicActions);
};

0 comments on commit e46c461

Please sign in to comment.