Skip to content

Commit

Permalink
Merge pull request #5999 from connext/5996-enforce-min
Browse files Browse the repository at this point in the history
fix: move max enforcement to utils
  • Loading branch information
preethamr authored Apr 8, 2024
2 parents 04a7d8e + 4328f42 commit c858a22
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 64 deletions.
19 changes: 0 additions & 19 deletions packages/agents/sdk/src/sdkBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -653,25 +653,6 @@ export class SdkBase extends SdkShared {
requestContext,
);

if (this.chainData) {
const maxRelayerFeeInNative = this.chainData.get(params.destinationDomain.toString())?.maxRelayerFeeInNative;
const maxRelayerFeeInNativeBN = BigNumber.from(maxRelayerFeeInNative ?? relayerFee);
if (params.priceIn === "usd") {
const scaleFactor = 10000; // leave some precision for low value assets
const scaledOriginNativeTokenPrice = originNativeTokenPrice * scaleFactor;

// Truncate remaining decimals and convert back to USD
const originNativeTokenPriceUsdBN = BigNumber.from(
scaledOriginNativeTokenPrice.toString().split('.')[0]
).div(scaleFactor);

const maxRelayerFeeInUsdBN = maxRelayerFeeInNativeBN.mul(originNativeTokenPriceUsdBN);
if (relayerFee.gt(maxRelayerFeeInUsdBN)) return maxRelayerFeeInUsdBN;
} else {
if (relayerFee.gt(maxRelayerFeeInNativeBN)) return maxRelayerFeeInNativeBN;
}
}

return relayerFee;
}

Expand Down
32 changes: 1 addition & 31 deletions packages/agents/sdk/test/sdkBase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ describe("SdkBase", () => {

beforeEach(() => {
calculateRelayerFeeStub = stub(SharedFns, "calculateRelayerFee");
getConversionRateStub = stub(SharedFns, "getConversionRate")
getConversionRateStub = stub(SharedFns, "getConversionRate");
});

afterEach(() => {
Expand All @@ -615,36 +615,6 @@ describe("SdkBase", () => {
expect(calculateRelayerFeeStub.callCount).to.be.eq(1);
expect(relayerFee.toString()).to.be.eq("100");
});

it("should return maxRelayerFeeInNative from chaindata if estimate is too high", async () => {
calculateRelayerFeeStub.resolves(BigNumber.from(100_000_000_000));
const relayerFee = await sdkBase.estimateRelayerFee({
originDomain: mock.domain.A,
destinationDomain: mock.domain.A,
});
expect(relayerFee.toString()).to.be.eq(sdkBase.chainData.get(mock.domain.A)!.maxRelayerFeeInNative);
});

it("should return converted fee from chaindata if estimate is too high and is in usd, ", async () => {
calculateRelayerFeeStub.resolves(BigNumber.from(100_000_000_000));
const relayerFee = await sdkBase.estimateRelayerFee({
originDomain: mock.domain.A,
destinationDomain: mock.domain.A,
priceIn: "usd",
originNativeTokenPrice: 100,
destinationNativeTokenPrice: 100,
});
expect(relayerFee.toString()).to.be.eq(BigNumber.from(100_000_000).toString());
});

it("should return high estimate if no override in chaindata", async () => {
calculateRelayerFeeStub.resolves(BigNumber.from(100_000_000_000));
const relayerFee = await sdkBase.estimateRelayerFee({
originDomain: mock.domain.A,
destinationDomain: mock.domain.B,
});
expect(relayerFee.toString()).to.be.eq(BigNumber.from(100_000_000_000).toString());
});
});

describe("#calculateAmountReceived", () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/utils/src/mocks/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const mock = {
analytics: [{ query: "http://example.com", health: "http://example.com" }],
maxLag: 10,
},
maxRelayerFeeInNative: "1000000"
maxRelayerFeeInEth: "1000000",
},
{
name: "Unit Test Chain 2",
Expand All @@ -107,6 +107,7 @@ export const mock = {
analytics: [{ query: "http://example.com", health: "http://example.com" }],
maxLag: 10,
},
maxRelayerFeeInEth: "1000000",
},
]),
signature: mkSig("0xabcdef1c"),
Expand Down
2 changes: 1 addition & 1 deletion packages/utils/src/peripherals/chainData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export type ChainData = {
messaging: string;
gasPriceFactor?: string;
};
maxRelayerFeeInNative: string;
maxRelayerFeeInEth: string;
};

// Helper method to reorganize this list into a mapping by chain ID for quicker lookup.
Expand Down
45 changes: 33 additions & 12 deletions packages/utils/src/peripherals/relayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,25 @@ export const calculateRelayerFee = async (
: undefined;

const totalGasAmount = callDataGasAmount ? Number(baseGasFees) + Number(callDataGasAmount) : Number(executeGasAmount);
const [estimatedRelayerFee, originTokenPrice, destinationTokenPrice] = await Promise.all([
const [estimatedRelayerFee, originTokenPrice, destinationTokenPrice, ethPrice] = await Promise.all([
// destination native asset fee
getGelatoEstimatedFee(
destinationChainId,
constants.AddressZero,
Number(totalGasAmount),
isHighPriority,
l1GasLimit,
),
// USDC per origin native
originNativeTokenPrice
? Promise.resolve(originNativeTokenPrice)
: safeGetConversionRate(originChainId, undefined, logger),
// USDC per destination native
destinationNativeTokenPrice
? Promise.resolve(destinationNativeTokenPrice)
: safeGetConversionRate(destinationChainId, undefined, logger),
// USDC per ETH
safeGetConversionRate(1, undefined, logger),
]);

// fallback with passed-in gas price or with callback
Expand Down Expand Up @@ -128,6 +133,7 @@ export const calculateRelayerFee = async (
destinationChainId,
executeGasAmount,
callDataGasAmount,
baseGasFees,
});
}

Expand All @@ -139,23 +145,38 @@ export const calculateRelayerFee = async (
}

// converstion rate is float-point number. we multiply by 1000 to be more precise
const impactedOriginTokenPrice = Math.floor(originTokenPrice * 1000);
const impactedDestinationTokenPrice = Math.floor(destinationTokenPrice * 1000);

let relayerFeeFinal;
if (priceIn === "native") {
relayerFeeFinal = bumpedFee.mul(impactedDestinationTokenPrice).div(impactedOriginTokenPrice);
} else {
relayerFeeFinal = bumpedFee.mul(impactedDestinationTokenPrice).div(1000);
}
const scale = 1000;
const impactedOriginTokenPrice = Math.floor(originTokenPrice * scale);
const impactedDestinationTokenPrice = Math.floor(destinationTokenPrice * scale);

// convert to usd value
// fee in USDC
// fee native destination * (USDC/destination native) = fee in USDC
const relayerFeeUsd = bumpedFee.mul(impactedDestinationTokenPrice).div(scale);

// assert the cap
// get the max fee by destination chain
const maxFeeEth = BigNumber.from(chainData.get(destinationDomain)?.maxRelayerFeeInEth ?? "0");
// convert to USDC if cap and price available
const maxFeeUsd =
!ethPrice || maxFeeEth.isZero() ? relayerFeeUsd : maxFeeEth.mul(Math.floor(ethPrice * scale)).div(scale);

const cappedFeeUsd = relayerFeeUsd.gt(maxFeeUsd) ? maxFeeUsd : relayerFeeUsd;
const final = priceIn !== "native" ? cappedFeeUsd : cappedFeeUsd.mul(scale).div(impactedOriginTokenPrice);

if (logger) {
logger.info("Fee estimation completed!", requestContext, methodContext, {
bumpedFee: bumpedFee.toString(),
originTokenPrice,
destinationTokenPrice,
relayerFeeInOriginNativeAsset: relayerFeeFinal.toString(),
ethPrice,
maxFeeEth,
priceIn,
relayerFeeUsd: relayerFeeUsd.toString(),
cappedFeeUsd: cappedFeeUsd.toString(),
final: final.toString(),
});
}
return relayerFeeFinal;

return final;
};
30 changes: 30 additions & 0 deletions packages/utils/test/peripherals/relayer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,36 @@ describe("Peripherals:Relayer", () => {
expect(estimatedRelayerFee).to.be.deep.eq(BigNumber.from("18000"));
});

it("should return correct capped value", async () => {
getConversionRateStub.onFirstCall().resolves(1);
getConversionRateStub.onSecondCall().resolves(150);
getConversionRateStub.onThirdCall().resolves(1);
getGelatoEstimatedFeeStub.resolves(BigNumber.from(10000));
const estimatedRelayerFee = await calculateRelayerFee(
{ originDomain: "13337", destinationDomain: "13338" },
mock.chainData(),
logger,
);
// estimatedRelayerFee = (estimatedGelatoFee + estimatedGelatoFee x buffer_percentage / 100) x ( destinationTokenPrice / originTokenPrice )
// ==> (10000 + 10000 x 20 / 100) x ( 150 / 1 ) = 1800000 > 1000000
expect(estimatedRelayerFee).to.be.deep.eq(BigNumber.from("1000000"));
});

it("should return correct capped value in native", async () => {
getConversionRateStub.onFirstCall().resolves(1);
getConversionRateStub.onSecondCall().resolves(150);
getConversionRateStub.onThirdCall().resolves(1);
getGelatoEstimatedFeeStub.resolves(BigNumber.from(10000));
const estimatedRelayerFee = await calculateRelayerFee(
{ originDomain: "13337", destinationDomain: "13338", priceIn: "native" },
mock.chainData(),
logger,
);
// estimatedRelayerFee = (estimatedGelatoFee + estimatedGelatoFee x buffer_percentage / 100) x ( destinationTokenPrice / originTokenPrice )
// ==> (10000 + 10000 x 20 / 100) x ( 150 / 1 ) = 1800000 > 1000000
expect(estimatedRelayerFee).to.be.deep.eq(BigNumber.from("1000000"));
});

it("should return correct value when provided token prices", async () => {
getGelatoEstimatedFeeStub.resolves(BigNumber.from(10000));
const estimatedRelayerFee = await calculateRelayerFee(
Expand Down

0 comments on commit c858a22

Please sign in to comment.