-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add 10% min on gas retries (#425)
* chore: add 10% min on gas retries * Make sure values are integers before casting to BN * bignumber * update default max gas for retries * gas * Add gas unit tests, bignumber helpers
- Loading branch information
Showing
5 changed files
with
219 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import { Transactions } from ".prisma/client"; | ||
import { getDefaultGasOverrides } from "@thirdweb-dev/sdk"; | ||
import { BigNumber, ethers, providers } from "ethers"; | ||
import { getGasSettingsForRetry } from "../utils/gas"; | ||
|
||
jest.mock("@thirdweb-dev/sdk"); | ||
const mockGetDefaultGasOverrides = | ||
getDefaultGasOverrides as jest.MockedFunction<typeof getDefaultGasOverrides>; | ||
|
||
describe("getGasSettingsForRetry", () => { | ||
let mockProvider: ethers.providers.StaticJsonRpcProvider; | ||
let mockTransaction: Transactions; | ||
|
||
beforeEach(() => { | ||
mockProvider = new providers.StaticJsonRpcProvider(); | ||
|
||
// @ts-ignore | ||
mockTransaction = { | ||
gasPrice: "1000", | ||
retryGasValues: false, | ||
maxFeePerGas: "500", | ||
maxPriorityFeePerGas: "100", | ||
}; | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it("new gas settings for legacy gas format", async () => { | ||
mockGetDefaultGasOverrides.mockResolvedValue({ | ||
gasPrice: BigNumber.from(1000), | ||
}); | ||
|
||
const gasSettings = await getGasSettingsForRetry( | ||
mockTransaction, | ||
mockProvider, | ||
); | ||
|
||
expect(gasSettings).toEqual({ | ||
gasPrice: BigNumber.from(2000), | ||
}); | ||
}); | ||
|
||
it("new gas settings for legacy gas format, fallback to 110% of previous attempt", async () => { | ||
mockGetDefaultGasOverrides.mockResolvedValue({ | ||
gasPrice: BigNumber.from(520), | ||
}); | ||
|
||
const gasSettings = await getGasSettingsForRetry( | ||
mockTransaction, | ||
mockProvider, | ||
); | ||
|
||
expect(gasSettings).toEqual({ | ||
gasPrice: BigNumber.from(1100), // uses 1100 instead of 1040 | ||
}); | ||
}); | ||
|
||
it("new gas settings for EIP 1559 gas format", async () => { | ||
mockGetDefaultGasOverrides.mockResolvedValueOnce({ | ||
maxFeePerGas: BigNumber.from(500), | ||
maxPriorityFeePerGas: BigNumber.from(100), | ||
}); | ||
|
||
const gasSettings = await getGasSettingsForRetry( | ||
mockTransaction, | ||
mockProvider, | ||
); | ||
|
||
expect(gasSettings).toEqual({ | ||
maxFeePerGas: BigNumber.from(1000), | ||
maxPriorityFeePerGas: BigNumber.from(200), | ||
}); | ||
}); | ||
|
||
it("new gas settings for EIP 1559 gas format, fallback to 110% of previous attempt", async () => { | ||
mockGetDefaultGasOverrides.mockResolvedValueOnce({ | ||
maxFeePerGas: BigNumber.from(500), | ||
maxPriorityFeePerGas: BigNumber.from(100), | ||
}); | ||
|
||
const gasSettings = await getGasSettingsForRetry( | ||
mockTransaction, | ||
mockProvider, | ||
); | ||
|
||
expect(gasSettings).toEqual({ | ||
maxFeePerGas: BigNumber.from(1000), | ||
maxPriorityFeePerGas: BigNumber.from(200), | ||
}); | ||
}); | ||
|
||
it("new gas settings for EIP 1559 gas format, use manual overrides", async () => { | ||
mockGetDefaultGasOverrides.mockResolvedValueOnce({ | ||
maxFeePerGas: BigNumber.from(500), | ||
maxPriorityFeePerGas: BigNumber.from(100), | ||
}); | ||
|
||
mockTransaction = { | ||
...mockTransaction, | ||
retryGasValues: true, | ||
retryMaxFeePerGas: "2222", | ||
retryMaxPriorityFeePerGas: "444", | ||
}; | ||
|
||
const gasSettings = await getGasSettingsForRetry( | ||
mockTransaction, | ||
mockProvider, | ||
); | ||
|
||
expect(gasSettings).toEqual({ | ||
maxFeePerGas: BigNumber.from(2222), | ||
maxPriorityFeePerGas: BigNumber.from(444), | ||
}); | ||
}); | ||
|
||
it("new gas settings for EIP 1559 gas format, manual overrides but fall back to 110% previous attempt", async () => { | ||
mockGetDefaultGasOverrides.mockResolvedValueOnce({ | ||
maxFeePerGas: BigNumber.from(500), | ||
maxPriorityFeePerGas: BigNumber.from(100), | ||
}); | ||
|
||
mockTransaction = { | ||
...mockTransaction, | ||
retryGasValues: true, | ||
retryMaxFeePerGas: "505", | ||
retryMaxPriorityFeePerGas: "105", | ||
}; | ||
|
||
const gasSettings = await getGasSettingsForRetry( | ||
mockTransaction, | ||
mockProvider, | ||
); | ||
|
||
expect(gasSettings).toEqual({ | ||
maxFeePerGas: BigNumber.from(550), | ||
maxPriorityFeePerGas: BigNumber.from(110), | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import type { BigNumber } from "ethers"; | ||
|
||
export const maxBN = (a: BigNumber, b: BigNumber) => (a.gt(b) ? a : b); | ||
export const minBN = (a: BigNumber, b: BigNumber) => (a.lt(b) ? a : b); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import type { Transactions } from ".prisma/client"; | ||
import { getDefaultGasOverrides } from "@thirdweb-dev/sdk"; | ||
import { BigNumber, providers } from "ethers"; | ||
import { maxBN } from "./bigNumber"; | ||
|
||
/** | ||
* | ||
* @param tx | ||
* @param provider | ||
* @returns | ||
*/ | ||
export const getGasSettingsForRetry = async ( | ||
tx: Transactions, | ||
provider: providers.StaticJsonRpcProvider, | ||
): ReturnType<typeof getDefaultGasOverrides> => { | ||
// Default: get gas settings from chain. | ||
const { gasPrice, maxFeePerGas, maxPriorityFeePerGas } = | ||
await getDefaultGasOverrides(provider); | ||
|
||
// Handle legacy gas format. | ||
if (gasPrice) { | ||
const newGasPrice = gasPrice.mul(2); | ||
// Gas settings must be 10% higher than a previous attempt. | ||
const minGasPrice = BigNumber.from(tx.gasPrice!).mul(110).div(100); | ||
|
||
return { | ||
gasPrice: maxBN(newGasPrice, minGasPrice), | ||
}; | ||
} | ||
|
||
// Handle EIP 1559 gas format. | ||
let newMaxFeePerGas = maxFeePerGas.mul(2); | ||
let newMaxPriorityFeePerGas = maxPriorityFeePerGas.mul(2); | ||
|
||
if (tx.retryGasValues) { | ||
// If this tx is manually retried, override with provided gas settings. | ||
newMaxFeePerGas = BigNumber.from(tx.retryMaxFeePerGas!); | ||
newMaxPriorityFeePerGas = BigNumber.from(tx.retryMaxPriorityFeePerGas!); | ||
} | ||
|
||
// Gas settings muset be 10% higher than a previous attempt. | ||
const minMaxFeePerGas = BigNumber.from(tx.maxFeePerGas!).mul(110).div(100); | ||
const minMaxPriorityFeePerGas = BigNumber.from(tx.maxPriorityFeePerGas!) | ||
.mul(110) | ||
.div(100); | ||
|
||
return { | ||
maxFeePerGas: maxBN(newMaxFeePerGas, minMaxFeePerGas), | ||
maxPriorityFeePerGas: maxBN( | ||
newMaxPriorityFeePerGas, | ||
minMaxPriorityFeePerGas, | ||
), | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters