Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve: simulate relay gas fees for messages #862

Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c9dc866
chore: add vercel as dev dep
james-a-morris Oct 5, 2023
403499e
improve: simulate relay gas fees
james-a-morris Oct 5, 2023
b3efc79
chore: revert for another PR
james-a-morris Oct 5, 2023
80cb76f
improve: throw error if recipient is not a contract
james-a-morris Oct 10, 2023
bb5925e
improve: change constructor imports
james-a-morris Oct 20, 2023
2ae0db4
chore: bump sdk
james-a-morris Oct 20, 2023
1abe39e
improve: ready for changes
james-a-morris Oct 20, 2023
8ab4e64
chore: wrap error
james-a-morris Oct 20, 2023
856bf67
chore: resolve check for hex string at first
james-a-morris Oct 23, 2023
e0ca8d2
improve: doc representation
james-a-morris Oct 23, 2023
a8e7c84
improve: error message
james-a-morris Oct 23, 2023
703312b
improve: add token caching
james-a-morris Oct 23, 2023
1d77eb5
chore: merge branch
james-a-morris Oct 24, 2023
bc6c8f7
chore: remove blockTag
james-a-morris Oct 24, 2023
6821268
improve: use dummy address
james-a-morris Oct 24, 2023
fef8c7a
improve: remove blocktag reference. Always "latest"
james-a-morris Oct 25, 2023
5b79022
chore: fix bug
james-a-morris Oct 25, 2023
351f102
improve: remove account balance
james-a-morris Oct 25, 2023
aba05f7
improve: use recipient ZERO_ADDRESS
james-a-morris Oct 25, 2023
ac085a4
chore: change out comment
james-a-morris Oct 25, 2023
fd5dc96
improve: modify UI call
james-a-morris Oct 25, 2023
1d6d0e6
chore: target block lag
james-a-morris Oct 25, 2023
b16d52d
chore: revise item
james-a-morris Oct 25, 2023
572ce1e
improve: use optional param
james-a-morris Oct 25, 2023
70bea1d
chore: revert optional params
james-a-morris Oct 25, 2023
11c8552
fix: invalid signature call
james-a-morris Oct 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 78 additions & 27 deletions api/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.MAINNET)
Expand All @@ -343,7 +342,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.OPTIMISM)
Expand All @@ -354,7 +352,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.POLYGON)
Expand All @@ -365,7 +362,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.ARBITRUM)
Expand All @@ -376,7 +372,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.ZK_SYNC)
Expand All @@ -387,7 +382,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.BASE)
Expand All @@ -399,7 +393,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.GOERLI)
Expand All @@ -410,7 +403,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.ARBITRUM_GOERLI)
Expand All @@ -421,7 +413,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.ZK_SYNC_GOERLI)
Expand All @@ -432,7 +423,6 @@ export const queries: Record<
undefined,
undefined,
undefined,
undefined,
REACT_APP_COINGECKO_PRO_API_KEY,
getLogger(),
getGasMarkup(CHAIN_IDs.BASE_GOERLI)
Expand Down Expand Up @@ -487,25 +477,58 @@ export const getTokenSymbol = (tokenAddress: string): string => {
* @param amount The amount of funds that are requesting to be transferred
* @param originChainId The origin chain that this token will be transferred from
* @param destinationChainId The destination chain that this token will be transferred to
* @param recipientAddress The address that will receive the transferred funds
* @param tokenPrice An optional overred price to prevent the SDK from creating its own call
* @param message An optional message to include in the transfer
* @param relayerAddress An optional relayer address to use for the transfer
* @returns The a promise to the relayer fee for the given `amount` of transferring `l1Token` to `destinationChainId`
*/
export const getRelayerFeeDetails = (
export const getRelayerFeeDetails = async (
l1Token: string,
amount: sdk.utils.BigNumberish,
originChainId: number,
destinationChainId: number,
tokenPrice?: number
recipientAddress = "0x893d0d70ad97717052e3aa8903d9615804167759", // A known dummy address
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this address hardcoded somewhere in the SDK?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it is and you use the sdk constants in suggested-fees. In that case, why not make this a required param?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only usage of constants in the SDK is for the RelayerAddress. This is the recipient & it was added so that we only had to specify the dummy address in one location.

I'm not sure adding a recipient address to the /limits would benefit the endpoint as this recipient is just needed to resolve the minimum deposit for a dummy fill request.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved offline ^

tokenPrice?: number,
message?: string,
relayerAddress?: string
): Promise<sdk.relayFeeCalculator.RelayerFeeDetails> => {
const tokenSymbol = getTokenSymbol(l1Token);
const tokenAddresses = sdk.utils.getL2TokenAddresses(l1Token);
if (!tokenAddresses) {
throw new InputError(
`Could not resolve token address for token ${l1Token}`
);
}
const originToken = tokenAddresses[originChainId];
const destinationToken = tokenAddresses[destinationChainId];

const relayFeeCalculator = getRelayerFeeCalculator(destinationChainId);
return relayFeeCalculator.relayerFeeDetails(
amount,
tokenSymbol,
tokenPrice,
originChainId.toString(),
destinationChainId.toString()
);
try {
return await relayFeeCalculator.relayerFeeDetails(
{
amount: sdk.utils.toBN(amount),
depositId: sdk.utils.bnUint32Max.toNumber(),
depositor: recipientAddress,
destinationChainId,
originChainId,
relayerFeePct: sdk.utils.bnZero,
realizedLpFeePct: sdk.utils.bnZero,
recipient: recipientAddress,
message: message ?? sdk.constants.EMPTY_MESSAGE,
quoteTimestamp: sdk.utils.getCurrentTime(),
originToken,
destinationToken,
},
amount,
relayerAddress,
tokenPrice
);
} catch (_e: unknown) {
// Resolve and transform the error
const e = _e as Error;
// We want to mask this error as an Input error.
throw new InputError(e?.message);
}
};

/**
Expand Down Expand Up @@ -589,23 +612,51 @@ export const isRouteEnabled = (
};

/**
* Resolves the balance of a given ERC20 token at a provided address
* Resolves the balance of a given ERC20 token at a provided address. If no token is provided, the balance of the
* native currency will be returned.
* @param chainId The blockchain Id to query against
* @param token The valid ERC20 token address on the given `chainId`
* @param account A valid Web3 wallet address
* @param blockTag A blockTag to specify a historical balance date
* @param token The valid ERC20 token address on the given `chainId`.
* @returns A promise that resolves to the BigNumber of the balance
*/
export const getBalance = (
chainId: string | number,
token: string,
account: string,
blockTag: number | "latest" = "latest"
token: string
): Promise<BigNumber> => {
return ERC20__factory.connect(token, getProvider(Number(chainId))).balanceOf(
return sdk.utils.getTokenBalance(
account,
{ blockTag }
token,
getProvider(Number(chainId))
);
};

/**
* Resolves the cached balance of a given ERC20 token at a provided address. If no token is provided, the balance of the
* native currency will be returned.
* @param chainId The blockchain Id to query against
* @param account A valid Web3 wallet address
* @param token The valid ERC20 token address on the given `chainId`.
* @returns A promise that resolves to the BigNumber of the balance
*/
export const getCachedTokenBalance = async (
chainId: string | number,
account: string,
token: string
): Promise<BigNumber> => {
// Make the request to the vercel API.
const response = await axios.get<{ balance: string }>(
`${resolveVercelEndpoint()}/api/account-balance`,
{
params: {
chainId,
account,
token,
},
}
);
// Return the balance
return BigNumber.from(response.data.balance);
};

/**
Expand Down
61 changes: 61 additions & 0 deletions api/account-balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { VercelResponse } from "@vercel/node";
import { assert, Infer, type, string } from "superstruct";
import { TypedVercelRequest } from "./_types";
import {
getBalance,
getLogger,
handleErrorCondition,
validAddress,
} from "./_utils";

const AccountBalanceQueryParamsSchema = type({
token: validAddress(),
account: validAddress(),
chainId: string(),
});

type AccountBalanceQueryParams = Infer<typeof AccountBalanceQueryParamsSchema>;

const handler = async (
{ query }: TypedVercelRequest<AccountBalanceQueryParams>,
response: VercelResponse
) => {
const logger = getLogger();
logger.debug({
at: "AccountBalance",
message: "Query data",
query,
});
try {
// Validate the query parameters
assert(query, AccountBalanceQueryParamsSchema);
// Deconstruct the query parameters
let { token, account, chainId } = query;
// Rely on the utils to query the balance of either the native
james-a-morris marked this conversation as resolved.
Show resolved Hide resolved
// token or an ERC20 token.
const balance = await getBalance(chainId, account, token);
// Package the response
const result = {
balance: balance.toString(),
account: account,
token: token,
};
// Log the response
logger.debug({
at: "AccountBalance",
message: "Response data",
responseJson: result,
});
// Set the caching headers that will be used by the CDN.
response.setHeader(
"Cache-Control",
"s-maxage=150, stale-while-revalidate=150"
);
// Return the response
response.status(200).json(result);
} catch (error: unknown) {
return handleErrorCondition("account-balance", response, logger, error);
}
};

export default handler;
19 changes: 5 additions & 14 deletions api/limits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
getRelayerFeeDetails,
getCachedTokenPrice,
getTokenDetails,
getBalance,
getCachedTokenBalance,
maxBN,
minBN,
isRouteEnabled,
Expand Down Expand Up @@ -149,34 +149,25 @@ const handler = async (
ethers.BigNumber.from("10").pow(18),
computedOriginChainId,
Number(destinationChainId),
undefined,
tokenPriceNative
),
hubPool.callStatic.multicall(multicallInput, { blockTag: BLOCK_TAG_LAG }),
Promise.all(
fullRelayers.map((relayer) =>
getBalance(
destinationChainId!,
destinationToken,
relayer,
BLOCK_TAG_LAG
)
getCachedTokenBalance(destinationChainId!, destinationToken, relayer)
)
),
Promise.all(
transferRestrictedRelayers.map((relayer) =>
getBalance(
destinationChainId!,
destinationToken,
relayer,
BLOCK_TAG_LAG
james-a-morris marked this conversation as resolved.
Show resolved Hide resolved
)
getCachedTokenBalance(destinationChainId!, destinationToken, relayer)
)
),
Promise.all(
fullRelayers.map((relayer) =>
destinationChainId === "1"
? ethers.BigNumber.from("0")
: getBalance("1", l1Token, relayer, BLOCK_TAG_LAG)
: getCachedTokenBalance("1", l1Token, relayer)
)
),
]);
Expand Down
Loading
Loading