Skip to content

Commit

Permalink
feat: implement evm execute (#380)
Browse files Browse the repository at this point in the history
* feat: implement evm execute

* chore: fix lint

* chore: support all evm chain in evm package

* chore: support missing chains for evm client in testnet

* chore: refactor

* chore: uncomment check for tx.approved

* chore: add tests

* docs(changeset): feat: add evmExecute for transaction-recovery package

* chore: add comments

* chore: add a bit more comment

* chore: fix lock files

* chore: merge pnpm-lock.yaml

* chore: fix test

* chore: remove .only
  • Loading branch information
npty authored Jul 10, 2024
1 parent b6506ad commit 1d7f53c
Show file tree
Hide file tree
Showing 16 changed files with 11,073 additions and 13,808 deletions.
7 changes: 7 additions & 0 deletions .changeset/violet-pets-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@axelarjs/transaction-recovery": patch
"@axelarjs/api": patch
"@axelarjs/evm": patch
---

feat: add evmExecute for transaction-recovery package
7 changes: 1 addition & 6 deletions packages/api/src/gmp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,6 @@ export type SearchGMPParams = Omit<BaseGMPParams, "contractMethod"> & {
};
};

type HexAmount = {
type: string;
hex: string;
};

export type SearchGMPCall = {
blockNumber: number;
blockHash: `0x${string}`;
Expand Down Expand Up @@ -101,7 +96,7 @@ export type SearchGMPCall = {
payloadHash: string;
payload: string;
symbol: string;
amount: HexAmount;
amount: string;
messageId?: string;
};
};
Expand Down
41 changes: 26 additions & 15 deletions packages/deposit-address/src/get-deposit-address/isomorphic.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ENVIRONMENTS } from "@axelarjs/core";
import { AXELAR_RPC_URLS_FALLBACK, ENVIRONMENTS } from "@axelarjs/core";

import * as mod from "../get-deposit-address/helpers/depositService";
import {
Expand Down Expand Up @@ -191,21 +191,32 @@ describe("Deposit Address", () => {
describe("requestConfigs", () => {
test("should allow users to customize an RPC endpoint", async () => {
vi.spyOn(mod, "getActiveChains");
const axelarRpcUrl = "https://axelar-testnet-rpc.qubelabs.io:443";
const params: SendOptions = {
sourceChain: "osmosis-7",
destinationChain: "ethereum-sepolia",
asset: "uaxl",
destinationAddress: "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
environment: ENVIRONMENTS.testnet,
requestConfig: { axelarRpcUrl },
};

const res = await getLinkedDepositAddress(params);

expect(res?.depositAddress).toBeTruthy();
const fallbackAxelarRpcUrls = AXELAR_RPC_URLS_FALLBACK.testnet;

let depositAddress, rpcUrl;
for (const axelarRpcUrl of fallbackAxelarRpcUrls) {
const params: SendOptions = {
sourceChain: "osmosis-7",
destinationChain: "ethereum-sepolia",
asset: "uaxl",
destinationAddress: "0xB8Cd93C83A974649D76B1c19f311f639e62272BC",
environment: ENVIRONMENTS.testnet,
requestConfig: { axelarRpcUrl },
};

const depositResponse = await getLinkedDepositAddress(params).catch(
() => undefined
);

if (!depositResponse) continue;

depositAddress = depositResponse?.depositAddress;
rpcUrl = axelarRpcUrl;
}

expect(depositAddress).toBeTruthy();
expect(mod.getActiveChains).toHaveBeenCalledWith("testnet", {
axelarRpcUrl,
axelarRpcUrl: rpcUrl,
});
});
});
1 change: 1 addition & 0 deletions packages/evm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
},
"devDependencies": {
"@axelar-network/interchain-token-service": "^1.2.4",
"@axelarjs/api": "workspace:^",
"@axelarjs/config": "workspace:*",
"@types/node": "^20.11.30",
"@types/prettier": "^2.7.3",
Expand Down
62 changes: 58 additions & 4 deletions packages/evm/src/clients/clients.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,68 @@
import { createAxelarConfigClient } from "@axelarjs/api";

import { avalancheFuji } from "viem/chains";

import { InterchainTokenClient } from "../contracts";
import { createPublicTestnetClient } from "./testnet-client";
import {
createPublicClient,
type SupportedMainnetChain,
} from "./mainnet-client";
import {
createPublicTestnetClient,
type SupportedTestnetChain,
} from "./testnet-client";

describe("EVM Clients", () => {
const client = createPublicTestnetClient("avalancheFuji");

it("shhould be defined", () => {
it("should be defined", () => {
const client = createPublicTestnetClient("avalanche");
expect(client).toBeDefined();
});

it("should support all mainnet chains", async () => {
const env = "mainnet";
const configClient = createAxelarConfigClient(env);
const configs = await configClient.getAxelarConfigs(env);
const chains = Object.keys(configs.chains).filter(
(chainId) => configs.chains[chainId]?.chainType === "evm"
);
const supportedChains = [];

chains.forEach((chain) => {
try {
createPublicClient(chain as SupportedMainnetChain);
supportedChains.push(chain);
} catch (e) {
console.error(
`Chain ${chain} is not supported in mainnet. Please fix it.`
);
}
});

expect(supportedChains.length).toBe(chains.length);
});

it("should support all testnet chains", async () => {
const env = "testnet";
const configClient = createAxelarConfigClient(env);
const configs = await configClient.getAxelarConfigs(env);
const chains = Object.keys(configs.chains).filter(
(chainId) => configs.chains[chainId]?.chainType === "evm"
);
const supportedChains = [];

chains.forEach((chain) => {
try {
createPublicTestnetClient(chain as SupportedTestnetChain);
supportedChains.push(chain);
} catch (e) {
console.error(
`Chain ${chain} is not supported in testnet. Please fix it.`
);
}
});

expect(supportedChains.length).toBe(chains.length - 1); // Excluding centrifuge-2 chain because public rpc is not available
});
});

describe("InterchainTokenClient", () => {
Expand Down
31 changes: 31 additions & 0 deletions packages/evm/src/clients/custom/centrifuge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineChain } from "viem";

export const centrifuge = defineChain({
id: 2090,
name: "Centrifuge",
network: "centrifuge",
nativeCurrency: { name: "Centrifuge", symbol: "CFG", decimals: 18 },
rpcUrls: {
default: {
http: ["https://fullnode.parachain.centrifuge.io"],
},
},
blockExplorers: {
default: {
name: "Centrifuge Explorer",
url: "https://centrifuge.subscan.io/",
},
},
});

export const centrifugeTestnet = defineChain({
id: 2032,
name: "Centrifuge Testnet",
network: "centrifuge",
nativeCurrency: { name: "Centrifuge", symbol: "CFG", decimals: 18 },
rpcUrls: {
default: {
http: [""], // TODO: Add testnet RPC URL
},
},
});
16 changes: 16 additions & 0 deletions packages/evm/src/clients/mainnet-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,35 @@ import {
avalanche,
base,
blast,
bsc,
celo,
fantom,
filecoin,
fraxtal,
immutableZkEvm,
kava,
linea,
mainnet,
mantle,
moonbeam,
optimism,
polygon,
scroll,
} from "viem/chains";

import { centrifuge } from "./custom/centrifuge";

/**
* Mainnet chains
*/
export const MAINNET_CHAINS = {
mainnet,
fantom,
centrifuge,
kava,
linea,
moonbeam,
polygon,
filecoin,
arbitrum,
aurora,
Expand All @@ -38,6 +51,9 @@ export const MAINNET_CHAINS = {
base,
blast,
fraxtal,
ethereum: mainnet,
binance: bsc,
immutable: immutableZkEvm,
} as const;

export type SupportedMainnetChain = keyof typeof MAINNET_CHAINS;
Expand Down
46 changes: 31 additions & 15 deletions packages/evm/src/clients/testnet-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,53 @@ import {
} from "viem";
import {
arbitrumSepolia,
auroraTestnet,
avalancheFuji,
baseSepolia,
blastSepolia,
bscTestnet,
celoAlfajores,
fantomTestnet,
filecoinHyperspace,
filecoinCalibration,
fraxtalTestnet,
immutableZkEvmTestnet,
kavaTestnet,
lineaTestnet,
mantleTestnet,
moonbaseAlpha,
optimismSepolia,
polygonMumbai,
scrollSepolia,
sepolia,
} from "viem/chains";

import { centrifugeTestnet } from "./custom/centrifuge";

/**
* Testnet chains
*/
export const TESTNET_CHAINS = {
sepolia,
arbitrumSepolia,
baseSepolia,
fantomTestnet,
filecoinHyperspace,
auroraTestnet,
avalancheFuji,
celoAlfajores,
mantleTestnet,
optimismSepolia,
scrollSepolia,
blastSepolia,
fraxtalTestnet,
"ethereum-sepolia": sepolia,
"arbitrum-sepolia": arbitrumSepolia,
"avalanche-fuji": avalancheFuji,
"base-sepolia": baseSepolia,
fantom: fantomTestnet,
"filecoin-2": filecoinCalibration,
"linea-sepolia": lineaTestnet,
"mantle-sepolia": mantleTestnet,
"optimism-sepolia": optimismSepolia,
immutable: immutableZkEvmTestnet,
linea: lineaTestnet,
moonbeam: moonbaseAlpha,
"polygon-sepolia": polygonMumbai,
polygon: polygonMumbai,
binance: bscTestnet,
avalanche: avalancheFuji,
"blast-sepolia": blastSepolia,
"centrifuge-2": centrifugeTestnet,
celo: celoAlfajores,
kava: kavaTestnet,
scroll: scrollSepolia,
fraxtal: fraxtalTestnet,
} as const;

export type SupportedTestnetChain = keyof typeof TESTNET_CHAINS;
Expand Down
3 changes: 2 additions & 1 deletion packages/transaction-recovery/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"author": "",
"license": "LicenseRef-LICENSE",
"devDependencies": {
"@axelarjs/api": "workspace:*",
"@axelarjs/config": "workspace:*",
"dotenv": "^16.4.5",
"gh-pages": "^6.1.1",
Expand All @@ -67,9 +68,9 @@
"vitest": "^1.4.0"
},
"dependencies": {
"@axelarjs/api": "workspace:*",
"@axelarjs/core": "workspace:*",
"@axelarjs/cosmos": "workspace:*",
"@axelarjs/evm": "workspace:^",
"@cosmjs/proto-signing": "^0.31.3",
"@cosmjs/stargate": "^0.31.3",
"viem": "^2.8.18"
Expand Down
24 changes: 16 additions & 8 deletions packages/transaction-recovery/src/common/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { EvmClientError } from "./error";
import { createWalletClient, custom, http, publicActions, type WalletClient } from "viem";
import {
createWalletClient,
custom,
http,
publicActions,
type Hex,
type WalletClient,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";

import { EvmClientError } from "./error";

import "viem/window";

/**
Expand All @@ -11,18 +20,17 @@ import "viem/window";
*/
export function getWalletClient(
rpcUrl: string,
privateKey?: `0x${string}`
privateKey?: string | undefined
): WalletClient {
if (typeof window !== "undefined" && !window.ethereum) {
throw EvmClientError.WALLET_CLIENT_NOT_FOUND;
}
if (!privateKey) {
const hasBrowserWallet = typeof window !== "undefined" && window.ethereum;

if (!hasBrowserWallet && !privateKey) {
throw EvmClientError.WALLET_CLIENT_NOT_FOUND;
}

if (privateKey) {
return createWalletClient({
account: privateKeyToAccount(privateKey),
account: privateKeyToAccount(privateKey as Hex),
transport: http(rpcUrl),
}).extend(publicActions);
} else {
Expand Down
26 changes: 26 additions & 0 deletions packages/transaction-recovery/src/evm-execute/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createAxelarscanClient } from "@axelarjs/api";
import { createAxelarConfigClient } from "@axelarjs/api/axelar-config";
import { createAxelarQueryClient } from "@axelarjs/api/axelar-query";
import { createGMPClient } from "@axelarjs/api/gmp";

import { evmExecute as baseEvmExecute } from "./isomorphic";
import type { EvmExecuteParams } from "./types";

/**
* A wrapper function for executing EVM transaction on the destination chain.
*
* @param params - An object containing parameters for the EVM execution, including the environment.
* @returns The result of the EVM execution.
*/
export function evmExecute(params: EvmExecuteParams) {
const { environment } = params;

return baseEvmExecute(params, {
axelarscanClient: createAxelarscanClient(environment),
axelarQueryClient: createAxelarQueryClient(environment),
configClient: createAxelarConfigClient(environment),
gmpClient: createGMPClient(environment),
});
}

export default evmExecute;
Loading

0 comments on commit 1d7f53c

Please sign in to comment.