Skip to content

Commit

Permalink
Use Iron Fish Bridge API (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
dguenther authored Sep 30, 2024
1 parent c03e15a commit 89c1b49
Show file tree
Hide file tree
Showing 27 changed files with 721 additions and 581 deletions.
20 changes: 0 additions & 20 deletions main/api/chainport/handleGetChainportTransactionStatus.ts

This file was deleted.

199 changes: 82 additions & 117 deletions main/api/chainport/index.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,43 @@
import axios from "axios";
import { z } from "zod";

import { handleGetChainportBridgeTransactionEstimatedFees } from "./handleGetChainportBridgeTransactionEstimatedFees";
import { handleGetChainportTransactionStatus } from "./handleGetChainportTransactionStatus";
import {
handleSendChainportBridgeTransaction,
handleSendChainportBridgeTransactionInput,
} from "./handleSendChainportBridgeTransaction";
import { buildTransactionRequestParamsInputs } from "./utils/buildTransactionRequestParams";
import { decodeChainportMemo } from "./utils/decodeChainportMemo";
import { getChainportEndpoints } from "./utils/getChainportEndpoints";
import {
ChainportBridgeTransaction,
ChainportTargetNetwork,
ChainportToken,
assertMetadataApiResponse,
fetchChainportBridgeTransaction,
fetchChainportNetworks,
fetchChainportTokenPaths,
fetchChainportTokens,
fetchChainportTransactionStatus,
} from "./vendor/requests";
import { ChainportNetwork } from "./vendor/types";
import {
assertTokenPathsApiResponse,
assertTokensApiResponse,
} from "../../../shared/chainport";
import { logger } from "../ironfish/logger";
import { manager } from "../manager";
import { t } from "../trpc";

export const chainportRouter = t.router({
getChainportTokens: t.procedure.query(async () => {
const { tokensEndpoint, metadataEndpoint } = await getChainportEndpoints();
const ironfish = await manager.getIronfish();
const rpcClient = await ironfish.rpcClient();
const network = await rpcClient.chain.getNetworkInfo();

try {
const [tokensResponse, metadataResponse] = await Promise.all([
axios.get(tokensEndpoint),
axios.get(metadataEndpoint),
]);

const tokensData = assertTokensApiResponse(tokensResponse.data);
const chainportMeta = assertMetadataApiResponse(metadataResponse.data);

const chainportTokens = tokensData.verified_tokens.map((token) => {
const targetNetworks: Array<ChainportTargetNetwork> =
token.target_networks
.map((networkId) => {
const networkDetails = chainportMeta.cp_network_ids[networkId];
if (!networkDetails) {
throw new Error(`Unknown network id: ${networkId}`);
}
return {
chainportNetworkId: networkId,
label: networkDetails.label,
networkIcon: networkDetails.network_icon,
chainId: networkDetails.chain_id,
value: networkDetails.chain_id
? networkDetails.chain_id.toString()
: "",
};
})
.filter((item) => {
return item.value !== null;
});

return {
chainportId: token.id,
ironfishId: token.web3_address,
symbol: token.symbol,
name: token.name,
decimals: token.decimals,
targetNetworks,
};
});

const tokenEntries = chainportTokens.map<[string, ChainportToken]>(
(token) => [token.ironfishId, token],
const tokensResponse = await fetchChainportTokens(
network.content.networkId,
);
const chainportTokensMap: Record<string, ChainportToken> =
Object.fromEntries(tokenEntries);
const tokensData = assertTokensApiResponse(tokensResponse);

const networksEntries = chainportTokens.flatMap((token) =>
token.targetNetworks.map<[string, ChainportTargetNetwork]>(
(network) => [network.value, network],
),
);
const chainportNetworksMap: Record<string, ChainportTargetNetwork> =
Object.fromEntries(networksEntries);
return {
chainportTokens,
chainportTokensMap,
chainportNetworksMap,
chainportTokensMap: Object.fromEntries(
tokensData.map((token) => [token.web3_address, token]),
),
};
} catch (err) {
logger.error(`Failed to fetch Chainport tokens data.
Expand All @@ -91,46 +47,68 @@ ${err}
throw err;
}
}),
getChainportTokenPaths: t.procedure
.input(
z.object({
tokenId: z.number(),
}),
)
.query(
async (opts): Promise<{ chainportTokenPaths: ChainportNetwork[] }> => {
const ironfish = await manager.getIronfish();
const rpcClient = await ironfish.rpcClient();
const network = await rpcClient.chain.getNetworkInfo();

try {
const tokenPathsResponse = await fetchChainportTokenPaths(
network.content.networkId,
opts.input.tokenId,
);
const tokenPathsData =
assertTokenPathsApiResponse(tokenPathsResponse);

return {
chainportTokenPaths: tokenPathsData,
};
} catch (err) {
logger.error(`Failed to fetch Chainport token paths data.
${err}
`);
throw err;
}
},
),
getChainportBridgeTransactionDetails: t.procedure
.input(
z.object({
amount: z.string(),
assetId: z.string(),
to: z.string(),
selectedNetwork: z.string(),
selectedNetwork: z.number(),
}),
)
.query(async (opts) => {
const endpoints = await getChainportEndpoints();
const ironfish = await manager.getIronfish();
const rpcClient = await ironfish.rpcClient();
const network = await rpcClient.chain.getNetworkInfo();

const { amount, assetId, to, selectedNetwork } = opts.input;
try {
return await fetchChainportBridgeTransaction(
network.content.networkId,
BigInt(amount),
assetId,
selectedNetwork,
to,
);
} catch (err) {
logger.error(`Failed to fetch Chainport bridge transaction details.
const url = `${
endpoints.baseUrl
}/ironfish/metadata?raw_amount=${amount.toString()}&asset_id=${assetId}&target_network_id=${selectedNetwork}&target_web3_address=${to}`;

const response = await fetch(url);
const data:
| ChainportBridgeTransaction
| {
error: {
code: string;
description: string;
};
} = await response.json();

if ("error" in data) {
throw new Error(data.error.description);
}

if (!response.ok) {
logger.error(`Failed to fetch chainport bridge transaction details.
${data}`);
throw new Error("A network error occured, please try again");
${err}
`);
throw err;
}

return data;
}),
getChainportBridgeTransactionEstimatedFees: t.procedure
.input(buildTransactionRequestParamsInputs)
Expand All @@ -150,36 +128,23 @@ ${data}`);
.input(
z.object({
transactionHash: z.string(),
baseNetworkId: z.number(),
}),
)
.query(async (opts) => {
const result = handleGetChainportTransactionStatus({
txHash: opts.input.transactionHash,
baseNetworkId: opts.input.baseNetworkId,
});
return result;
const ironfish = await manager.getIronfish();
const rpcClient = await ironfish.rpcClient();
const network = await rpcClient.chain.getNetworkInfo();

return await fetchChainportTransactionStatus(
network.content.networkId,
opts.input.transactionHash,
);
}),
getChainportMeta: t.procedure.query(async () => {
const { metadataEndpoint } = await getChainportEndpoints();
const response = await axios.get(metadataEndpoint);
const data = assertMetadataApiResponse(response.data);
return data;
getChainportNetworks: t.procedure.query(async () => {
const ironfish = await manager.getIronfish();
const rpcClient = await ironfish.rpcClient();
const network = await rpcClient.chain.getNetworkInfo();

return await fetchChainportNetworks(network.content.networkId);
}),
decodeMemo: t.procedure
.input(
z.object({
memo: z.string(),
}),
)
.query(async (opts) => {
try {
const result = decodeChainportMemo(
Buffer.from(opts.input.memo).toString(),
);
return result;
} catch (_err) {
return null;
}
}),
});
33 changes: 0 additions & 33 deletions main/api/chainport/utils/decodeChainportMemo.ts

This file was deleted.

34 changes: 0 additions & 34 deletions main/api/chainport/utils/getChainportEndpoints.ts

This file was deleted.

3 changes: 3 additions & 0 deletions main/api/chainport/vendor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Note

This code is shared with [the Iron Fish CLI](https://github.com/iron-fish/ironfish/tree/master/ironfish-cli/src/utils/chainport). Please propagate changes to that repository as well.
41 changes: 41 additions & 0 deletions main/api/chainport/vendor/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

import { MAINNET, TESTNET } from "@ironfish/sdk";

const config = {
[TESTNET.id]: {
endpoint: "https://testnet.api.ironfish.network",
outgoingAddresses: new Set([
"06102d319ab7e77b914a1bd135577f3e266fd82a3e537a02db281421ed8b3d13",
"db2cf6ec67addde84cc1092378ea22e7bb2eecdeecac5e43febc1cb8fb64b5e5",
"3be494deb669ff8d943463bb6042eabcf0c5346cf444d569e07204487716cb85",
]),
incomingAddresses: new Set([
"06102d319ab7e77b914a1bd135577f3e266fd82a3e537a02db281421ed8b3d13",
]),
},
[MAINNET.id]: {
endpoint: "https://api.ironfish.network",
outgoingAddresses: new Set([
"576ffdcc27e11d81f5180d3dc5690294941170d492b2d9503c39130b1f180405",
"7ac2d6a59e19e66e590d014af013cd5611dc146e631fa2aedf0ee3ed1237eebe",
]),
incomingAddresses: new Set([
"1216302193e8f1ad020f458b54a163039403d803e98673c6a85e59b5f4a1a900",
]),
},
};

export const isNetworkSupportedByChainport = (networkId: number) => {
return !!config[networkId];
};

export const getConfig = (networkId: number) => {
if (!config[networkId]) {
throw new Error(`Unsupported network ${networkId} for chainport`);
}

return config[networkId];
};
Loading

0 comments on commit 89c1b49

Please sign in to comment.