Skip to content

Commit

Permalink
message id recovery release (#432)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Santiago Giaccobasso <santigiacco@192.168.0.100>
Co-authored-by: Santiago Giaccobasso <santigiacco@MacBook-Pro.local>
  • Loading branch information
5 people authored Sep 2, 2024
1 parent 1f59883 commit 37245c8
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/wild-adults-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@axelarjs/maestro": patch
---

create a trpc to recover deployment message ids and use it in token details page to automatically check and recover the missing ids.
2 changes: 2 additions & 0 deletions apps/maestro/src/server/routers/interchainToken/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { getTokenManagerABI } from "./getTokenManagerABI";
import { recordInterchainTokenDeployment } from "./recordInterchainTokenDeployment";
import { recordRemoteTokensDeployment } from "./recordRemoteTokensDeployment";
import { recoverCanonicalTokenByTokenId } from "./recoverCanonicalTokenByTokenId";
import { recoverDeploymentMessageIdByTokenId } from "./recoverDeploymentMessageIdByTokenId";
import { searchInterchainToken } from "./searchInterchainToken";
import { setInterchainTokenIconUrl } from "./setInterchainTokenIconUrl";

Expand All @@ -30,6 +31,7 @@ export const interchainTokenRouter = router({
recordRemoteTokensDeployment,
findInterchainTokenByTokenId,
recoverCanonicalTokenByTokenId,
recoverDeploymentMessageIdByTokenId,
setInterchainTokenIconUrl,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { z } from "zod";

import { hex64Literal } from "~/lib/utils/validation";
import { protectedProcedure } from "~/server/trpc";

export const recoverDeploymentMessageIdByTokenId = protectedProcedure
.input(
z.object({
tokenId: hex64Literal(),
})
)
.mutation(async ({ ctx, input }) => {
// constraint: token must be in db
const { postgres } = ctx.persistence;

const token = await postgres.getInterchainTokenByTokenId(input.tokenId);

if (!token) {
throw new Error("Token not found");
}

// is missing deploymentMessageId?
if (!token.deploymentMessageId) {
// try to find deployment tx hash from indexed events limiting by the token deployment timestamp

const fromTime = (token.createdAt as Date).getTime() / 1000;
const bufferLength = 60 * 60 * 8; // 8 hours
const toTime = fromTime + bufferLength;

const deployments = await ctx.services.gmp.searchGMP({
contractMethod: ["InterchainTokenDeploymentStarted"],
_source: {
excludes: [
"refunded",
"refund_nonce",
"receipt",
"gas",
"approved",
"gas_price_rate",
"fees",
"gas_paid",
"to_refund",
"confirm",
"command_id",
"time_spent",
"executed.receipt",
"executed.transaction",
],
includes: [
"status",
"call.transactionHash",
"call.returnValues.destinationChain",
"interchain_token_deployment_started.tokenId",
],
},
fromTime: fromTime,
toTime: toTime,
size: 1000,
});

const validEntries = deployments.filter(
(x) =>
x.interchain_token_deployment_started &&
x.interchain_token_deployment_started.tokenId === input.tokenId
);

const results = validEntries.map((x) => {
const logIndex = x.call.logIndex ?? x.call._logIndex ?? 0;
return {
status: x.status,
destinationChain: x.call.returnValues.destinationChain,
txHash: x.call.transactionHash,
logIndex,
deploymentMessageId: `${x.call.transactionHash}-${logIndex}`,
};
});

if (results.length) {
await postgres.updateInterchainTokenDeploymentMessageId(
input.tokenId,
results[0].deploymentMessageId
);

for (const result of results) {
await postgres.updateRemoteInterchainTokenDeploymentMessageId(
input.tokenId,
result.destinationChain,
result.deploymentMessageId
);
}

return "updated";
}
}
});
26 changes: 26 additions & 0 deletions apps/maestro/src/services/db/postgres/MaestroPostgresClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,32 @@ export default class MaestroPostgresClient {
return await query;
}

async updateInterchainTokenDeploymentMessageId(
tokenId: string,
deploymentMessageId: string
) {
await this.db
.update(interchainTokens)
.set({ deploymentMessageId, updatedAt: new Date() })
.where(eq(interchainTokens.tokenId, tokenId));
}

async updateRemoteInterchainTokenDeploymentMessageId(
tokenId: string,
axealrChainId: string,
deploymentMessageId: string
) {
await this.db
.update(remoteInterchainTokens)
.set({ deploymentMessageId, updatedAt: new Date() })
.where(
and(
eq(remoteInterchainTokens.tokenId, tokenId),
eq(remoteInterchainTokens.axelarChainId, axealrChainId)
)
);
}

/**
* Returns the interchain token with the given `chainId` and `tokenAddress`,
* including its remote interchain tokens.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { GMPTxStatus } from "@axelarjs/api/gmp";
import { Alert, Button, cn, Tooltip } from "@axelarjs/ui";
import { Maybe } from "@axelarjs/utils";
import { useSessionStorageState } from "@axelarjs/utils/react";
import { useEffect, useMemo, type FC } from "react";
import { useCallback, useEffect, useMemo, type FC } from "react";

import { concat, isEmpty, map, partition, uniq, without } from "rambda";
import { useAccount, useBalance, useChainId, useSwitchChain } from "wagmi";
Expand Down Expand Up @@ -110,11 +110,14 @@ const ConnectedInterchainTokensPage: FC<ConnectedInterchainTokensPageProps> = (
tokenAddress: props.tokenAddress,
});

const { data: tokenDetails, error: tokenDetailsError } =
trpc.erc20.getERC20TokenDetails.useQuery({
chainId: props.chainId,
tokenAddress: props.tokenAddress,
});
const {
data: tokenDetails,
error: tokenDetailsError,
refetch: refetchTokenDetails,
} = trpc.erc20.getERC20TokenDetails.useQuery({
chainId: props.chainId,
tokenAddress: props.tokenAddress,
});

const [sessionState, setSessionState] = useInterchainTokenDetailsPageState({
chainId: props.chainId,
Expand Down Expand Up @@ -189,6 +192,40 @@ const ConnectedInterchainTokensPage: FC<ConnectedInterchainTokensPageProps> = (
}
}, [hasFetchedStatuses, setSessionState, statuses, statusesByChain]);

const utils = trpc.useUtils();

const { mutateAsync, isPending } =
trpc.interchainToken.recoverDeploymentMessageIdByTokenId.useMutation();

// If the token does not have a deployment message id, try to
// recover it and store it in the db then update the token details
const recoverMessageId = useCallback(() => {
if (!props.deploymentMessageId && props.tokenId && !isPending) {
void mutateAsync({ tokenId: props.tokenId }).then((result) => {
if (result === "updated") {
void utils.erc20.invalidate().then(() => void refetchTokenDetails());
void utils.interchainToken
.invalidate()
.then(() => void refetchInterchainToken());
}
});
}
}, [
isPending,
mutateAsync,
props.deploymentMessageId,
props.tokenId,
refetchInterchainToken,
refetchTokenDetails,
utils.erc20,
utils.interchainToken,
]);

// Try to recover deployment message id if it's missing
useEffect(() => {
recoverMessageId();
}, [recoverMessageId]);

const remoteChainsExecuted = useMemo(
() =>
Object.entries(statusesByChain)
Expand Down

0 comments on commit 37245c8

Please sign in to comment.