Skip to content

Commit 44dda57

Browse files
authored
Merge branch 'master' into chore/gckms-utils
2 parents e99d563 + 8f6617e commit 44dda57

File tree

7 files changed

+87
-31
lines changed

7 files changed

+87
-31
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"dependencies": {
1313
"@across-protocol/constants": "^3.1.22",
1414
"@across-protocol/contracts": "^3.0.18",
15-
"@across-protocol/sdk": "^3.3.25",
15+
"@across-protocol/sdk": "^3.3.26",
1616
"@arbitrum/sdk": "^4.0.2",
1717
"@consensys/linea-sdk": "^0.2.1",
1818
"@defi-wonderland/smock": "^2.3.5",

src/clients/ProfitClient.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
TOKEN_SYMBOLS_MAP,
2929
TOKEN_EQUIVALENCE_REMAPPING,
3030
ZERO_ADDRESS,
31+
formatGwei,
3132
} from "../utils";
3233
import { Deposit, DepositWithBlock, L1Token, SpokePoolClientsByChain } from "../interfaces";
3334
import { getAcrossHost } from "./AcrossAPIClient";
@@ -58,6 +59,7 @@ export type FillProfit = {
5859
grossRelayerFeeUsd: BigNumber; // USD value of the relay fee paid by the user.
5960
nativeGasCost: BigNumber; // Cost of completing the fill in the units of gas.
6061
tokenGasCost: BigNumber; // Cost of completing the fill in the relevant gas token.
62+
gasPrice: BigNumber; // Gas price in wei.
6163
gasPadding: BigNumber; // Positive padding applied to nativeGasCost and tokenGasCost before profitability.
6264
gasMultiplier: BigNumber; // Gas multiplier applied to fill cost estimates before profitability.
6365
gasTokenPriceUsd: BigNumber; // Price paid per unit of gas the gas token in USD.
@@ -213,7 +215,7 @@ export class ProfitClient {
213215
deposit,
214216
notificationPath: "across-unprofitable-fills",
215217
});
216-
return { nativeGasCost: uint256Max, tokenGasCost: uint256Max };
218+
return { nativeGasCost: uint256Max, tokenGasCost: uint256Max, gasPrice: uint256Max };
217219
}
218220
}
219221

@@ -229,15 +231,21 @@ export class ProfitClient {
229231
return this._getTotalGasCost(deposit, this.relayerAddress);
230232
}
231233

234+
getGasCostsForChain(chainId: number): TransactionCostEstimate {
235+
return this.totalGasCosts[chainId];
236+
}
237+
232238
// Estimate the gas cost of filling this relay.
233239
async estimateFillCost(
234240
deposit: Deposit
235-
): Promise<Pick<FillProfit, "nativeGasCost" | "tokenGasCost" | "gasTokenPriceUsd" | "gasCostUsd">> {
241+
): Promise<Pick<FillProfit, "nativeGasCost" | "tokenGasCost" | "gasTokenPriceUsd" | "gasCostUsd" | "gasPrice">> {
236242
const { destinationChainId: chainId } = deposit;
237243

238244
const gasToken = this.resolveGasToken(chainId);
239245
const gasTokenPriceUsd = this.getPriceOfToken(gasToken.symbol);
240-
let { nativeGasCost, tokenGasCost } = await this.getTotalGasCost(deposit);
246+
const totalGasCost = await this.getTotalGasCost(deposit);
247+
let { nativeGasCost, tokenGasCost } = totalGasCost;
248+
const gasPrice = totalGasCost.gasPrice;
241249

242250
Object.entries({
243251
"gas consumption": nativeGasCost, // raw gas units
@@ -265,6 +273,7 @@ export class ProfitClient {
265273
return {
266274
nativeGasCost,
267275
tokenGasCost,
276+
gasPrice,
268277
gasTokenPriceUsd,
269278
gasCostUsd,
270279
};
@@ -363,7 +372,9 @@ export class ProfitClient {
363372
: bnZero;
364373

365374
// Estimate the gas cost of filling this relay.
366-
const { nativeGasCost, tokenGasCost, gasTokenPriceUsd, gasCostUsd } = await this.estimateFillCost(deposit);
375+
const { nativeGasCost, tokenGasCost, gasTokenPriceUsd, gasCostUsd, gasPrice } = await this.estimateFillCost(
376+
deposit
377+
);
367378

368379
// Determine profitability. netRelayerFeePct effectively represents the capital cost to the relayer;
369380
// i.e. how much it pays out to the recipient vs. the net fee that it receives for doing so.
@@ -386,6 +397,7 @@ export class ProfitClient {
386397
grossRelayerFeeUsd,
387398
nativeGasCost,
388399
tokenGasCost,
400+
gasPrice,
389401
gasPadding: this.gasPadding,
390402
gasMultiplier: this.resolveGasMultiplier(deposit),
391403
gasTokenPriceUsd,
@@ -434,7 +446,7 @@ export class ProfitClient {
434446

435447
this.logger.debug({
436448
at: "ProfitClient#getFillProfitability",
437-
message: `${l1Token.symbol} v3 deposit ${depositId} with repayment on ${repaymentChainId} is ${profitable}`,
449+
message: `${l1Token.symbol} deposit ${depositId} with repayment on ${repaymentChainId} is ${profitable}`,
438450
deposit,
439451
inputTokenPriceUsd: formatEther(fill.inputTokenPriceUsd),
440452
inputTokenAmountUsd: formatEther(fill.inputAmountUsd),
@@ -445,6 +457,7 @@ export class ProfitClient {
445457
grossRelayerFeePct: `${formatFeePct(fill.grossRelayerFeePct)}%`,
446458
nativeGasCost: fill.nativeGasCost,
447459
tokenGasCost: formatEther(fill.tokenGasCost),
460+
gasPrice: formatGwei(fill.gasPrice.toString()),
448461
gasPadding: this.gasPadding,
449462
gasMultiplier: formatEther(this.resolveGasMultiplier(deposit)),
450463
gasTokenPriceUsd: formatEther(fill.gasTokenPriceUsd),
@@ -465,13 +478,14 @@ export class ProfitClient {
465478
lpFeePct: BigNumber,
466479
l1Token: L1Token,
467480
repaymentChainId: number
468-
): Promise<Pick<FillProfit, "profitable" | "nativeGasCost" | "tokenGasCost" | "netRelayerFeePct">> {
481+
): Promise<Pick<FillProfit, "profitable" | "nativeGasCost" | "gasPrice" | "tokenGasCost" | "netRelayerFeePct">> {
469482
let profitable = false;
470483
let netRelayerFeePct = bnZero;
471484
let nativeGasCost = uint256Max;
472485
let tokenGasCost = uint256Max;
486+
let gasPrice = uint256Max;
473487
try {
474-
({ profitable, netRelayerFeePct, nativeGasCost, tokenGasCost } = await this.getFillProfitability(
488+
({ profitable, netRelayerFeePct, nativeGasCost, tokenGasCost, gasPrice } = await this.getFillProfitability(
475489
deposit,
476490
lpFeePct,
477491
l1Token,
@@ -490,6 +504,7 @@ export class ProfitClient {
490504
profitable: profitable || (this.isTestnet && nativeGasCost.lt(uint256Max)),
491505
nativeGasCost,
492506
tokenGasCost,
507+
gasPrice,
493508
netRelayerFeePct,
494509
};
495510
}

src/relayer/Relayer.ts

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
TransactionResponse,
2222
ZERO_ADDRESS,
2323
Profiler,
24+
formatGwei,
2425
} from "../utils";
2526
import { RelayerClients } from "./RelayerClientHelper";
2627
import { RelayerConfig } from "./RelayerConfig";
@@ -36,6 +37,7 @@ type BatchLPFees = { [depositKey: string]: RepaymentFee[] };
3637
type RepaymentChainProfitability = {
3738
gasLimit: BigNumber;
3839
gasCost: BigNumber;
40+
gasPrice: BigNumber;
3941
relayerFeePct: BigNumber;
4042
lpFeePct: BigNumber;
4143
};
@@ -673,7 +675,13 @@ export class Relayer {
673675
l1Token,
674676
lpFees
675677
);
676-
const { relayerFeePct, gasCost, gasLimit: _gasLimit, lpFeePct: realizedLpFeePct } = repaymentChainProfitability;
678+
const {
679+
relayerFeePct,
680+
gasCost,
681+
gasLimit: _gasLimit,
682+
lpFeePct: realizedLpFeePct,
683+
gasPrice,
684+
} = repaymentChainProfitability;
677685
if (!isDefined(repaymentChainId)) {
678686
profitClient.captureUnprofitableFill(deposit, realizedLpFeePct, relayerFeePct, gasCost);
679687
} else {
@@ -702,7 +710,7 @@ export class Relayer {
702710
tokenClient.decrementLocalBalance(destinationChainId, outputToken, outputAmount);
703711

704712
const gasLimit = isMessageEmpty(resolveDepositMessage(deposit)) ? undefined : _gasLimit;
705-
this.fillRelay(deposit, repaymentChainId, realizedLpFeePct, gasLimit);
713+
this.fillRelay(deposit, repaymentChainId, realizedLpFeePct, gasPrice, gasLimit);
706714
}
707715
} else if (selfRelay) {
708716
// Prefer exiting early here to avoid fast filling any deposits we send. This approach assumes that we always
@@ -720,7 +728,14 @@ export class Relayer {
720728
// relayer is both the depositor and the recipient, because a deposit on a cheap SpokePool chain could cause
721729
// expensive fills on (for example) mainnet.
722730
const { lpFeePct } = lpFees.find((lpFee) => lpFee.paymentChainId === destinationChainId);
723-
this.fillRelay(deposit, destinationChainId, lpFeePct);
731+
// For self-relays, gas price is not a concern because we are bypassing profitability requirements so
732+
// use profit client's gasprice.
733+
this.fillRelay(
734+
deposit,
735+
destinationChainId,
736+
lpFeePct,
737+
this.clients.profitClient.getGasCostsForChain(destinationChainId).gasPrice
738+
);
724739
} else {
725740
// TokenClient.getBalance returns that we don't have enough balance to submit the fast fill.
726741
// At this point, capture the shortfall so that the inventory manager can rebalance the token inventory.
@@ -973,7 +988,13 @@ export class Relayer {
973988
this.setFillStatus(deposit, FillStatus.RequestedSlowFill);
974989
}
975990

976-
fillRelay(deposit: Deposit, repaymentChainId: number, realizedLpFeePct: BigNumber, gasLimit?: BigNumber): void {
991+
fillRelay(
992+
deposit: Deposit,
993+
repaymentChainId: number,
994+
realizedLpFeePct: BigNumber,
995+
gasPrice: BigNumber,
996+
gasLimit?: BigNumber
997+
): void {
977998
const { spokePoolClients } = this.clients;
978999
this.logger.debug({
9791000
at: "Relayer::fillRelay",
@@ -1005,7 +1026,7 @@ export class Relayer {
10051026
];
10061027

10071028
const message = `Filled v3 deposit ${messageModifier}🚀`;
1008-
const mrkdwn = this.constructRelayFilledMrkdwn(deposit, repaymentChainId, realizedLpFeePct);
1029+
const mrkdwn = this.constructRelayFilledMrkdwn(deposit, repaymentChainId, realizedLpFeePct, gasPrice);
10091030
const contract = spokePoolClients[deposit.destinationChainId].spokePool;
10101031
const chainId = deposit.destinationChainId;
10111032
const multiCallerClient = this.getMulticaller(chainId);
@@ -1056,6 +1077,7 @@ export class Relayer {
10561077
repaymentChainProfitability: {
10571078
gasLimit: bnZero,
10581079
gasCost: bnUint256Max,
1080+
gasPrice: bnUint256Max,
10591081
relayerFeePct: bnZero,
10601082
lpFeePct: bnUint256Max,
10611083
},
@@ -1086,17 +1108,25 @@ export class Relayer {
10861108
const getRepaymentChainProfitability = async (
10871109
preferredChainId: number,
10881110
lpFeePct: BigNumber
1089-
): Promise<{ profitable: boolean; gasLimit: BigNumber; gasCost: BigNumber; relayerFeePct: BigNumber }> => {
1111+
): Promise<{
1112+
profitable: boolean;
1113+
gasLimit: BigNumber;
1114+
gasCost: BigNumber;
1115+
gasPrice: BigNumber;
1116+
relayerFeePct: BigNumber;
1117+
}> => {
10901118
const {
10911119
profitable,
10921120
nativeGasCost: gasLimit,
10931121
tokenGasCost: gasCost,
1122+
gasPrice,
10941123
netRelayerFeePct: relayerFeePct, // net relayer fee is equal to total fee minus the lp fee.
10951124
} = await profitClient.isFillProfitable(deposit, lpFeePct, hubPoolToken, preferredChainId);
10961125
return {
10971126
profitable,
10981127
gasLimit,
10991128
gasCost,
1129+
gasPrice,
11001130
relayerFeePct,
11011131
};
11021132
};
@@ -1116,10 +1146,11 @@ export class Relayer {
11161146
// @dev The following internal function should be the only one used to set `preferredChain` above.
11171147
const getProfitabilityDataForPreferredChainIndex = (preferredChainIndex: number): RepaymentChainProfitability => {
11181148
const lpFeePct = lpFeePcts[preferredChainIndex];
1119-
const { gasLimit, gasCost, relayerFeePct } = repaymentChainProfitabilities[preferredChainIndex];
1149+
const { gasLimit, gasCost, relayerFeePct, gasPrice } = repaymentChainProfitabilities[preferredChainIndex];
11201150
return {
11211151
gasLimit,
11221152
gasCost,
1153+
gasPrice,
11231154
relayerFeePct,
11241155
lpFeePct,
11251156
};
@@ -1344,9 +1375,14 @@ export class Relayer {
13441375
}
13451376
}
13461377

1347-
private constructRelayFilledMrkdwn(deposit: Deposit, repaymentChainId: number, realizedLpFeePct: BigNumber): string {
1378+
private constructRelayFilledMrkdwn(
1379+
deposit: Deposit,
1380+
repaymentChainId: number,
1381+
realizedLpFeePct: BigNumber,
1382+
gasPrice: BigNumber
1383+
): string {
13481384
let mrkdwn =
1349-
this.constructBaseFillMarkdown(deposit, realizedLpFeePct) +
1385+
this.constructBaseFillMarkdown(deposit, realizedLpFeePct, gasPrice) +
13501386
` Relayer repayment: ${getNetworkName(repaymentChainId)}.`;
13511387

13521388
if (isDepositSpedUp(deposit)) {
@@ -1361,7 +1397,7 @@ export class Relayer {
13611397
return mrkdwn;
13621398
}
13631399

1364-
private constructBaseFillMarkdown(deposit: Deposit, _realizedLpFeePct: BigNumber): string {
1400+
private constructBaseFillMarkdown(deposit: Deposit, _realizedLpFeePct: BigNumber, _gasPriceGwei: BigNumber): string {
13651401
const { symbol, decimals } = this.clients.hubPoolClient.getTokenInfoForDeposit(deposit);
13661402
const srcChain = getNetworkName(deposit.originChainId);
13671403
const dstChain = getNetworkName(deposit.destinationChainId);
@@ -1380,7 +1416,9 @@ export class Relayer {
13801416
const _outputAmount = createFormatFunction(2, 4, false, outputTokenDecimals)(deposit.outputAmount.toString());
13811417
msg +=
13821418
` and output ${_outputAmount} ${outputTokenSymbol}, with depositor ${depositor}.` +
1383-
` Realized LP fee: ${realizedLpFeePct}%, total fee: ${totalFeePct}%.`;
1419+
` Realized LP fee: ${realizedLpFeePct}%, total fee: ${totalFeePct}%. Gas price used in profit calc: ${formatGwei(
1420+
_gasPriceGwei.toString()
1421+
)} Gwei.`;
13841422

13851423
return msg;
13861424
}

src/utils/SDKUtils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const {
4040
formatFeePct,
4141
shortenHexStrings,
4242
convertFromWei,
43+
formatGwei,
4344
max,
4445
min,
4546
utf8ToHex,

test/ProfitClient.ConsiderProfitability.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,13 @@ describe("ProfitClient: Consider relay profit", () => {
7878

7979
// Randomise the fillRelay cost in units of gas.
8080
const nativeGasCost = toBN(random(80_000, 100_000));
81-
const tokenGasCost = nativeGasCost.mul(toGWei(random(1, 100))).div(toBN(10).pow(9));
81+
const gasPrice = toGWei(random(1, 100));
82+
const tokenGasCost = nativeGasCost.mul(gasPrice).div(toBN(10).pow(9));
8283

8384
profitClient.setTokenPrice(gasToken.address, gasTokenPriceUsd);
84-
profitClient.setGasCost(chainId, { nativeGasCost, tokenGasCost });
85+
profitClient.setGasCost(chainId, { nativeGasCost, tokenGasCost, gasPrice });
8586

86-
return { nativeGasCost, tokenGasCost, gasTokenPriceUsd };
87+
return { nativeGasCost, tokenGasCost, gasPrice, gasTokenPriceUsd };
8788
};
8889

8990
const tokens = Object.fromEntries(
@@ -106,8 +107,9 @@ describe("ProfitClient: Consider relay profit", () => {
106107
chainIds.map((chainId) => {
107108
const nativeGasCost = toBN(100_000); // Assume 100k gas for a single fill
108109
const gasTokenPrice = toBN(chainId);
109-
const tokenGasCost = nativeGasCost.mul(gasTokenPrice);
110-
return [chainId, { nativeGasCost, tokenGasCost }];
110+
const gasPrice = gasTokenPrice;
111+
const tokenGasCost = nativeGasCost.mul(gasPrice);
112+
return [chainId, { nativeGasCost, tokenGasCost, gasPrice }];
111113
})
112114
);
113115

@@ -296,12 +298,11 @@ describe("ProfitClient: Consider relay profit", () => {
296298
});
297299

298300
it("Considers gas cost when computing profitability", async () => {
299-
const gasPrice = toGWei(10);
300301
const gasCostMultipliers = ["0.1", "0.5", "1", "2", "5", "10"].map((n) => toBNWei(n));
301302

302303
for (const originChainId of chainIds) {
303304
for (const destinationChainId of chainIds.filter((chainId) => chainId !== originChainId)) {
304-
const { nativeGasCost: baseNativeGasCost } = gasCost[destinationChainId];
305+
const { nativeGasCost: baseNativeGasCost, gasPrice } = gasCost[destinationChainId];
305306

306307
for (const token of Object.values(tokens)) {
307308
const inputToken = randomAddress();
@@ -339,7 +340,7 @@ describe("ProfitClient: Consider relay profit", () => {
339340
const nativeGasCost = baseNativeGasCost.mul(gasCostMultiplier).div(fixedPoint);
340341
const tokenGasCost = nativeGasCost.mul(gasPrice);
341342
const gasCostUsd = tokenGasCost.mul(gasTokenPriceUsd).div(fixedPoint);
342-
profitClient.setGasCost(destinationChainId, { nativeGasCost, tokenGasCost });
343+
profitClient.setGasCost(destinationChainId, { nativeGasCost, tokenGasCost, gasPrice });
343344

344345
const gasCostPct = gasCostUsd.mul(fixedPoint).div(outputAmountUsd);
345346

test/mocks/MockProfitClient.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class MockProfitClient extends ProfitClient {
5757
const defaultGasCost = {
5858
nativeGasCost: defaultFillCost,
5959
tokenGasCost: defaultGasPrice.mul(defaultFillCost),
60+
gasPrice: defaultGasPrice,
6061
};
6162
Object.values(spokePoolClients).map(({ chainId }) => {
6263
this.setGasCost(chainId, defaultGasCost); // gas/fill

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@
5353
yargs "^17.7.2"
5454
zksync-web3 "^0.14.3"
5555

56-
"@across-protocol/sdk@^3.3.25":
57-
version "3.3.25"
58-
resolved "https://registry.yarnpkg.com/@across-protocol/sdk/-/sdk-3.3.25.tgz#6eec255fb7a1025050e0415b56f1bf8681936b1e"
59-
integrity sha512-nBBrXY/kslvfsYnVd6kTNOuDSomlfRTw6v4uI40au/rEzPQ6G8X5d/F+DGN3iPfi3ltHY5BEiqE+E6s7AxHA8A==
56+
"@across-protocol/sdk@^3.3.26":
57+
version "3.3.26"
58+
resolved "https://registry.yarnpkg.com/@across-protocol/sdk/-/sdk-3.3.26.tgz#b33aaf1545af9ff2969dd99db5ac39f3d785f689"
59+
integrity sha512-RaEkwtme9k24NAQDKWTFaywrhQd7RqxRRitRwSXoiGm6Aw4iOXHoh/CjT3Z1wjcCBxHVeRozYUXESQlJZ+dOTw==
6060
dependencies:
6161
"@across-protocol/across-token" "^1.0.0"
6262
"@across-protocol/constants" "^3.1.22"

0 commit comments

Comments
 (0)