Skip to content

Commit d48d298

Browse files
committed
refactor: remove PythContractFactory class
1 parent 0f8a1f4 commit d48d298

File tree

4 files changed

+116
-168
lines changed

4 files changed

+116
-168
lines changed

Diff for: apps/price_pusher/src/evm/command.ts

+16-11
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import * as options from "../options";
55
import { readPriceConfigFile } from "../price-config";
66
import { PythPriceListener } from "../pyth-price-listener";
77
import { Controller } from "../controller";
8-
import { EvmPriceListener, EvmPricePusher, PythContractFactory } from "./evm";
8+
import { EvmPriceListener, EvmPricePusher } from "./evm";
99
import { getCustomGasStation } from "./custom-gas-station";
1010
import pino from "pino";
11+
import { createClient } from "./super-wallet";
12+
import { createPythContract } from "./pyth-contract";
13+
import { isWsEndpoint } from "../utils";
1114

1215
export default {
1316
command: "evm",
@@ -121,21 +124,22 @@ export default {
121124
logger.child({ module: "PythPriceListener" })
122125
);
123126

124-
const pythContractFactory = await PythContractFactory.create(
125-
endpoint,
126-
mnemonic,
127-
pythContractAddress
128-
);
127+
const client = await createClient(endpoint, mnemonic);
128+
const pythContract = createPythContract(client, pythContractAddress);
129129

130130
logger.info(
131-
`Pushing updates from wallet address: ${
132-
pythContractFactory.getAccount().address
133-
}`
131+
`Pushing updates from wallet address: ${client.account.address}`
134132
);
135133

134+
// It is possible to watch the events in the non-ws endpoints, either by getFilter
135+
// or by getLogs, but it is very expensive and our polling mechanism does it
136+
// in a more efficient way. So we only do it with ws endpoints.
137+
const watchEvents = isWsEndpoint(endpoint);
138+
136139
const evmListener = new EvmPriceListener(
137-
pythContractFactory,
140+
pythContract,
138141
priceItems,
142+
watchEvents,
139143
logger.child({ module: "EvmPriceListener" }),
140144
{
141145
pollingFrequency,
@@ -149,7 +153,8 @@ export default {
149153
);
150154
const evmPusher = new EvmPricePusher(
151155
priceServiceConnection,
152-
pythContractFactory,
156+
client,
157+
pythContract,
153158
logger.child({ module: "EvmPricePusher" }),
154159
overrideGasPriceMultiplier,
155160
overrideGasPriceMultiplierCap,

Diff for: apps/price_pusher/src/evm/evm.ts

+11-157
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
} from "../utils";
1313
import { PythAbi } from "./pyth-abi";
1414
import { Logger } from "pino";
15-
import { isWsEndpoint } from "../utils";
1615
import {
1716
PriceServiceConnection,
1817
HexString,
@@ -21,94 +20,38 @@ import {
2120
import { CustomGasStation } from "./custom-gas-station";
2221
import { PushAttempt } from "../common";
2322
import {
24-
PublicClient,
25-
Transport,
26-
WalletClient,
27-
createPublicClient,
28-
createWalletClient,
29-
getContract,
30-
defineChain,
31-
http,
32-
webSocket,
33-
Address,
34-
GetContractReturnType,
3523
WatchContractEventOnLogsParameter,
3624
TransactionExecutionError,
37-
Account,
3825
BaseError,
3926
ContractFunctionRevertedError,
4027
FeeCapTooLowError,
4128
InternalRpcError,
4229
InsufficientFundsError,
43-
Chain,
44-
publicActions,
45-
Client,
46-
RpcSchema,
47-
WalletActions,
48-
PublicActions,
4930
} from "viem";
5031

51-
import { mnemonicToAccount } from "viem/accounts";
52-
import * as chains from "viem/chains";
53-
54-
type PythContract = GetContractReturnType<
55-
typeof PythAbi,
56-
PublicClient | WalletClient
57-
>;
58-
59-
export type SuperWalletClient<
60-
transport extends Transport = Transport,
61-
chain extends Chain | undefined = Chain,
62-
account extends Account | undefined = Account
63-
> = Client<
64-
transport,
65-
chain,
66-
account,
67-
RpcSchema,
68-
PublicActions<transport, chain, account> & WalletActions<chain, account>
69-
>;
70-
71-
const UNKNOWN_CHAIN_CONFIG = {
72-
name: "Unknown",
73-
nativeCurrency: {
74-
name: "Unknown",
75-
symbol: "Unknown",
76-
decimals: 18,
77-
},
78-
rpcUrls: {
79-
default: {
80-
http: [],
81-
},
82-
},
83-
};
32+
import { PythContract } from "./pyth-contract";
33+
import { SuperWalletClient } from "./super-wallet";
8434

8535
export class EvmPriceListener extends ChainPriceListener {
86-
private pythContractFactory: PythContractFactory;
87-
private pythContract: PythContract;
88-
private logger: Logger;
89-
9036
constructor(
91-
pythContractFactory: PythContractFactory,
37+
private pythContract: PythContract,
9238
priceItems: PriceItem[],
93-
logger: Logger,
39+
private watchEvents: boolean,
40+
private logger: Logger,
9441
config: {
9542
pollingFrequency: DurationInSeconds;
9643
}
9744
) {
9845
super(config.pollingFrequency, priceItems);
9946

100-
this.pythContractFactory = pythContractFactory;
101-
this.pythContract = this.pythContractFactory.createPythContract();
47+
this.pythContract = pythContract;
10248
this.logger = logger;
10349
}
10450

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

182125
export class EvmPricePusher implements IPricePusher {
183-
private customGasStation?: CustomGasStation;
184-
private client: SuperWalletClient;
185-
private pythContract: PythContract;
186126
private pusherAddress: `0x${string}` | undefined;
187127
private lastPushAttempt: PushAttempt | undefined;
188128

189129
constructor(
190130
private connection: PriceServiceConnection,
191-
pythContractFactory: PythContractFactory,
131+
private client: SuperWalletClient,
132+
private pythContract: PythContract,
192133
private logger: Logger,
193134
private overrideGasPriceMultiplier: number,
194135
private overrideGasPriceMultiplierCap: number,
195136
private updateFeeMultiplier: number,
196137
private gasLimit?: number,
197-
customGasStation?: CustomGasStation
198-
) {
199-
this.customGasStation = customGasStation;
200-
this.pythContract = pythContractFactory.createPythContract();
201-
this.client = pythContractFactory.createClient();
202-
}
138+
private customGasStation?: CustomGasStation
139+
) {}
203140

204141
// The pubTimes are passed here to use the values that triggered the push.
205142
// This is an optimization to avoid getting a newer value (as an update comes)
@@ -468,86 +405,3 @@ export class EvmPricePusher implements IPricePusher {
468405
);
469406
}
470407
}
471-
472-
export class PythContractFactory {
473-
private endpoint: string;
474-
private mnemonic: string;
475-
private pythContractAddress: Address;
476-
private chainId: number;
477-
478-
private constructor(
479-
endpoint: string,
480-
mnemonic: string,
481-
pythContractAddress: Address,
482-
chainId: number
483-
) {
484-
this.endpoint = endpoint;
485-
this.mnemonic = mnemonic;
486-
this.pythContractAddress = pythContractAddress;
487-
this.chainId = chainId;
488-
}
489-
490-
static async create(
491-
endpoint: string,
492-
mnemonic: string,
493-
pythContractAddress: Address
494-
): Promise<PythContractFactory> {
495-
const chainId = await createPublicClient({
496-
transport: PythContractFactory.getTransport(endpoint),
497-
}).getChainId();
498-
return new PythContractFactory(
499-
endpoint,
500-
mnemonic,
501-
pythContractAddress,
502-
chainId
503-
);
504-
}
505-
506-
/**
507-
* This method creates a web3 Pyth contract with payer (based on mnemonic).
508-
*
509-
* @returns Pyth contract
510-
*/
511-
createPythContract(): PythContract {
512-
return getContract({
513-
address: this.pythContractAddress,
514-
abi: PythAbi,
515-
client: this.createClient(),
516-
});
517-
}
518-
519-
hasWebsocketProvider(): boolean {
520-
return isWsEndpoint(this.endpoint);
521-
}
522-
523-
getAccount(): Account {
524-
return mnemonicToAccount(this.mnemonic);
525-
}
526-
527-
createClient(): SuperWalletClient {
528-
return createWalletClient({
529-
transport: PythContractFactory.getTransport(this.endpoint),
530-
account: mnemonicToAccount(this.mnemonic),
531-
chain: PythContractFactory.getChain(this.chainId),
532-
}).extend(publicActions);
533-
}
534-
535-
// Get the chain corresponding to the chainId. If the chain is not found, it will return
536-
// an unknown chain which should work fine in most of the cases. We might need to update
537-
// the viem package to support new chains if they don't work as expected with the unknown
538-
// chain.
539-
private static getChain(chainId: number): Chain {
540-
return (
541-
Object.values(chains).find((chain) => chain.id === chainId) ||
542-
defineChain({ id: chainId, ...UNKNOWN_CHAIN_CONFIG })
543-
);
544-
}
545-
546-
private static getTransport(endpoint: string): Transport {
547-
if (isWsEndpoint(endpoint)) {
548-
return webSocket(endpoint);
549-
} else {
550-
return http(endpoint);
551-
}
552-
}
553-
}

Diff for: apps/price_pusher/src/evm/pyth-contract.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { getContract, Address, GetContractReturnType } from "viem";
2+
import { PythAbi } from "./pyth-abi";
3+
import { SuperWalletClient } from "./super-wallet";
4+
5+
export type PythContract = GetContractReturnType<
6+
typeof PythAbi,
7+
SuperWalletClient
8+
>;
9+
10+
export const createPythContract = (
11+
client: SuperWalletClient,
12+
address: Address
13+
): PythContract =>
14+
getContract({
15+
client,
16+
abi: PythAbi,
17+
address,
18+
});

Diff for: apps/price_pusher/src/evm/super-wallet.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {
2+
createPublicClient,
3+
createWalletClient,
4+
defineChain,
5+
http,
6+
webSocket,
7+
Account,
8+
Chain,
9+
publicActions,
10+
Client,
11+
RpcSchema,
12+
WalletActions,
13+
PublicActions,
14+
WebSocketTransport,
15+
HttpTransport,
16+
Transport,
17+
} from "viem";
18+
import { mnemonicToAccount } from "viem/accounts";
19+
import * as chains from "viem/chains";
20+
import { isWsEndpoint } from "../utils";
21+
22+
const UNKNOWN_CHAIN_CONFIG = {
23+
name: "Unknown",
24+
nativeCurrency: {
25+
name: "Unknown",
26+
symbol: "Unknown",
27+
decimals: 18,
28+
},
29+
rpcUrls: {
30+
default: {
31+
http: [],
32+
},
33+
},
34+
};
35+
36+
export type SuperWalletClient = Client<
37+
Transport,
38+
Chain,
39+
Account,
40+
RpcSchema,
41+
PublicActions<Transport, Chain, Account> & WalletActions<Chain, Account>
42+
>;
43+
44+
// Get the transport based on the endpoint
45+
const getTransport = (endpoint: string): WebSocketTransport | HttpTransport =>
46+
isWsEndpoint(endpoint) ? webSocket(endpoint) : http(endpoint);
47+
48+
// Get the chain corresponding to the chainId. If the chain is not found, it will return
49+
// an unknown chain which should work fine in most of the cases. We might need to update
50+
// the viem package to support new chains if they don't work as expected with the unknown
51+
// chain.
52+
const getChainById = (chainId: number): Chain =>
53+
Object.values(chains).find((chain) => chain.id === chainId) ||
54+
defineChain({ id: chainId, ...UNKNOWN_CHAIN_CONFIG });
55+
56+
export const createClient = async (
57+
endpoint: string,
58+
mnemonic: string
59+
): Promise<SuperWalletClient> => {
60+
const transport = getTransport(endpoint);
61+
62+
const chainId = await createPublicClient({
63+
transport,
64+
}).getChainId();
65+
66+
return createWalletClient({
67+
transport,
68+
account: mnemonicToAccount(mnemonic),
69+
chain: getChainById(chainId),
70+
}).extend(publicActions);
71+
};

0 commit comments

Comments
 (0)