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

feat: 'working' implementation of addgas complete #25

Merged
merged 4 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion packages/api/src/gmp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type BaseGMPParams = {

export type SearchGMPParams = BaseGMPParams & {
contractMethod?: "callContrct" | "callContrctWithToken";
txHash?: `0x${string}`;
txHash?: string | `0x${string}`;
txLogIndex?: number;
status?: GMPTxStatus;
from?: number;
Expand Down
15 changes: 14 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
export const ENVIRONMENTS = {
testnet: "testnet",
mainnet: "mainnet",
devnet: "devnet",
} as const;

export type Environment = keyof typeof ENVIRONMENTS;

export const COSMOS_GAS_RECEIVER_OPTIONS = {
testnet: "axelar1zl3rxpp70lmte2xr6c4lgske2fyuj3hupcsvcd",
mainnet: "axelar1aythygn6z5thymj6tmzfwekzh05ewg3l7d6y89",
} as const;

export type CosmosGasReceiver = keyof typeof COSMOS_GAS_RECEIVER_OPTIONS;

export const AXELARSCAN_API_URLS = {
testnet: "https://testnet.api.gmp.axelarscan.io",
mainnet: "https://api.gmp.axelarscan.io",
} as const;

export type AxelarscanUrl = keyof typeof AXELARSCAN_API_URLS;
9 changes: 7 additions & 2 deletions packages/transaction-recovery/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@
"author": "",
"license": "LicenseRef-LICENSE",
"devDependencies": {
"@axelarjs/config": "workspace:*",
"@axelarjs/cosmos": "workspace:*",
"@axelarjs/api": "workspace:*",
"@axelarjs/config": "workspace:*",
"dotenv": "^16.3.1",
"rimraf": "^5.0.1",
"typescript": "^5.2.2",
"vitest": "^0.34.3"
},
"dependencies": {
"@axelarjs/core": "workspace:*",
"@axelarjs/cosmos": "workspace:*",
"@cosmjs/proto-signing": "^0.30.1",
"@cosmjs/stargate": "^0.31.1"
}
}
63 changes: 51 additions & 12 deletions packages/transaction-recovery/src/addGas.spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,59 @@
import { createAxelarSigningClient } from "@axelarjs/cosmos/signing-client";
import { ENVIRONMENTS } from "@axelarjs/core";

import addGas from "./addGas";
import { vi } from "vitest";

const TX_HASH = `A6516262B303AF6D5D1599F46B52D2ED47DC1C1FFA3E56822A4313916C9AC8C4`;
import addGas, { SendOptions } from "./addGas";

const MOCK_ADD_GAS_RESPONSE = {
code: 0,
height: 2561692,
txIndex: 6,
events: [[]],
rawLog: "",
transactionHash:
"448337FAAAA9EAD528BB9D6B1DD32BDD5171988A87B9C5D1F8578D9927714918",
msgResponses: [],
gasUsed: 130739,
gasWanted: 250000,
};

vi.mock("./cosmosSigner/signer", () => {
const mockAddGasSignAndBroadcast = () => MOCK_ADD_GAS_RESPONSE;

const getCosmosSigner = vi.fn(() => ({
signAndBroadcast: vi.fn().mockImplementation(mockAddGasSignAndBroadcast),
}));

return {
getCosmosSigner,
};
});

describe("addGas", () => {
test("do stuff", async () => {
const signingClient = await createAxelarSigningClient({
environment: "testnet",
axelarRpcUrl: "https://axelartest-rpc.quickapi.com",
cosmosBasedWalletDetails: {
mnemonic: process.env["COSMOS_WALLET_MNEMONIC"] || "",
test("broadcast an IBC transfer", async () => {
const txHash =
"6118C285B0C7A139C5636184BECBF8C201FF36B61F44060B82EFE4C535084D9C";

const token = {
denom:
"ibc/9463E39D230614B313B487836D13A392BD1731928713D4C8427A083627048DB3",
amount: "1",
};

const sendOptions: SendOptions = {
txFee: {
gas: "250000",
amount: [{ denom: "uosmo", amount: "30000" }],
},
options: {},
});
channelIdToAxelar: "channel-3",
rpcUrl: "https://rpc.osmotest5.osmosis.zone",
environment: ENVIRONMENTS.testnet,
cosmosAddressPrefix: "osmo",
cosmosWalletMnemonic: process.env["COSMOS_WALLET_MNEMONIC"] as string,
};

const res = await addGas(txHash, token, sendOptions);

await addGas(signingClient, TX_HASH);
expect(res).toEqual(MOCK_ADD_GAS_RESPONSE);
});
});
80 changes: 64 additions & 16 deletions packages/transaction-recovery/src/addGas.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,73 @@
import { createGMPNodeClient } from "@axelarjs/api/gmp/node";
import type { AxelarSigningClient } from "@axelarjs/cosmos/signing-client";
import { COSMOS_GAS_RECEIVER_OPTIONS, Environment } from "@axelarjs/core";

const gmpClient = createGMPNodeClient({
prefixUrl: "https://testnet.api.gmp.axelarscan.io",
});
import type { Coin, StdFee } from "@cosmjs/stargate";

async function addGas(signingClient: AxelarSigningClient, txHash: string) {
const tx = await signingClient.getTx(txHash);
console.log({ tx });
import { getCosmosSigner, getCosmosWallet } from "./cosmosSigner";
import { gmpClient } from "./services";

const fees = await gmpClient.getFees({
destinationChain: "ethereum-2",
sourceChain: "polygon",
});
export type SendOptions = {
channelIdToAxelar: string;
rpcUrl: string;
txFee: StdFee;
timeoutTimestamp?: number;
environment: Environment;
cosmosAddressPrefix: string;
cosmosWalletMnemonic: string;
};

console.log({ fees });
async function addGas(
txHash: string,
token: Coin | "autocalculate",
sendOptions: SendOptions
) {
if (token === "autocalculate") {
throw new Error("autocalculate not yet supported, but we will soon!");
}

// send ibc transfer with memo field using message ID from tx above
// todo
const tx = await gmpClient(sendOptions.environment)
.searchGMP({
txHash,
})
.catch(() => undefined);

return {};
if (!tx || tx?.length < 1) {
throw new Error(`${txHash} could not be found`);
}

const offlineSigner = await getCosmosWallet(
sendOptions.cosmosWalletMnemonic,
sendOptions.cosmosAddressPrefix
);

const sender = await offlineSigner
.getAccounts()
.then(([acc]) => acc?.address);

if (!sender) {
throw new Error("Sender could not be found");
}

const signer = await getCosmosSigner(sendOptions.rpcUrl, offlineSigner);

return signer.signAndBroadcast(
sender,
[
{
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: "transfer",
sourceChannel: sendOptions.channelIdToAxelar,
token,
sender,
receiver: COSMOS_GAS_RECEIVER_OPTIONS[sendOptions.environment],
timeoutTimestamp:
sendOptions.timeoutTimestamp ?? (Date.now() + 90) * 1e9,
memo: tx[0]?.call.id,
},
},
],
sendOptions.txFee
);
}

export default addGas;
2 changes: 2 additions & 0 deletions packages/transaction-recovery/src/cosmosSigner/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./offlineSigner";
export * from "./signer";
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";

export async function getCosmosWallet(mnemonic: string, prefix: string) {
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix });
}
9 changes: 9 additions & 0 deletions packages/transaction-recovery/src/cosmosSigner/signer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { OfflineDirectSigner } from "@cosmjs/proto-signing";
import { SigningStargateClient } from "@cosmjs/stargate";

export const getCosmosSigner = async (
rpcUrl: string,
offlineDirectSigner: OfflineDirectSigner
) => {
return SigningStargateClient.connectWithSigner(rpcUrl, offlineDirectSigner);
};
7 changes: 7 additions & 0 deletions packages/transaction-recovery/src/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createGMPNodeClient } from "@axelarjs/api/gmp/node";
import { AXELARSCAN_API_URLS, Environment } from "@axelarjs/core";

export const gmpClient = (env: Environment) =>
createGMPNodeClient({
prefixUrl: AXELARSCAN_API_URLS[env],
});
Loading
Loading