Skip to content
Open
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
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@
"command": "ethcode.rental.create",
"title": "Create new ERC4907 contract",
"category": "Ethcode"
},
{
"command": "ethcode.payable.set",
"title": "Set payable value",
"category": "Ethcode"
}
],
"keybindings": [
Expand Down
6 changes: 6 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
callContractMethod,
deployContract,
displayBalance,
setPayableValue,
setTransactionGas,
updateSelectedNetwork,
} from "./utils/networks";
Expand Down Expand Up @@ -112,6 +113,11 @@ export async function activate(context: vscode.ExtensionContext) {
importKeyPair(context);
}),

//set payable value
commands.registerCommand("ethcode.payable.set", async () => {
setPayableValue(context);
}),

// Set custom gas estimate
// commands.registerCommand('ethcode.transaction.gas.set', async () => {
// const gasInp: InputBoxOptions = {
Expand Down
6 changes: 6 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ export interface ERC4907ContractType {
contract: string;
ERC4907Contract: string;
}

export interface useContractType {
contractName: string;
abiItemName: string;
contractAddress: any;
}
// Typeguard

export function isConstructorInputValue(
Expand Down
25 changes: 25 additions & 0 deletions src/utils/contractCall/ImmutableFunctionCall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { JsonFragment } from "@ethersproject/abi";
import { ethers } from "ethers";
import { ExtensionContext } from "vscode";
import { useContractType } from "../../types";
import { getSelectedProvider } from "../networks";
import { logger } from "../../lib";

export const ImmutableFunctionCall = async (
context: ExtensionContext,
params: any[],
abi: readonly JsonFragment[],
useContract: useContractType
) => {
const { abiItemName, contractName, contractAddress } = useContract;
const contract = new ethers.Contract(
contractAddress,
abi,
getSelectedProvider(context)
);
const result = await contract[abiItemName](...params);
logger.success(`Calling ${contractName} : ${abiItemName} --> Success!`);
if (result) {
logger.log(JSON.stringify(result));
}
};
54 changes: 54 additions & 0 deletions src/utils/contractCall/mutableFunctionCall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ethers } from "ethers";
import { ExtensionContext } from "vscode";
import { logger } from "../../lib";
import { EstimateGas, useContractType } from "../../types";
import { getGasEstimates } from "../functions";
import {
getConfiguration,
getSelectedNetConf,
getSignedContract,
} from "../networks";

export const MutableFunctionCall = async (
Copy link
Member

Choose a reason for hiding this comment

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

function names should always start with a smallcap letter.

context: ExtensionContext,
state: string,
Copy link
Member

Choose a reason for hiding this comment

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

state param should be renamed to stateMutability.

params: any[],
useContract: useContractType
) => {
const MAX_FEE_PER_GAS = 100;
const { abiItemName, contractAddress, contractName } = useContract;

const contract = await getSignedContract(context, contractAddress);

let gasCondition = (await context.workspaceState.get("gas")) as string;

const gasEstimate: EstimateGas | undefined = await getGasEstimates(
gasCondition,
context
);

const maxFeePerGas =
gasEstimate !== undefined ? gasEstimate.maxFeePerGas : MAX_FEE_PER_GAS;

const settingsGasLimit = (await getConfiguration().get("gasLimit")) as number;

const value =
Copy link
Member

Choose a reason for hiding this comment

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

When we create the input JSON for payable functions create the JSON with a value field. We should get all the necessary parameters from the input `JSON. Also optionally keep this input.

state === "payable" ? await context.workspaceState.get("payableValue") : 0;

const result = await contract[abiItemName as string](...params, {
value: value,
gasPrice: ethers.utils.parseUnits(maxFeePerGas.toString(), "gwei"),
gasLimit: settingsGasLimit,
});

logger.success("Waiting for confirmation...");

await result.wait();
logger.success("Transaction confirmed!");
logger.success(`Calling ${contractName} : ${abiItemName} --> Success!`);
logger.success(
`You can see detail of this transaction here. ${
getSelectedNetConf(context).blockScanner
}/tx/${result.hash}`
);
};
131 changes: 63 additions & 68 deletions src/utils/networks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ethers } from "ethers";
import * as vscode from "vscode";
import { window } from "vscode";
import { window, InputBoxOptions } from "vscode";
import {
CompiledJSONOutput,
GasEstimateOutput,
Expand All @@ -9,7 +9,12 @@ import {
} from "../types/output";
import { logger } from "../lib";
import { extractPvtKey } from "./wallet";
import { INetworkQP, EstimateGas, NetworkConfig } from "../types";
import {
INetworkQP,
EstimateGas,
NetworkConfig,
useContractType,
} from "../types";
import {
getConstructorInputs,
getDeployedInputs,
Expand All @@ -19,6 +24,8 @@ import {

import { errors } from "../config/errors";
import { selectContract } from "./contracts";
import { ImmutableFunctionCall } from "./contractCall/ImmutableFunctionCall";
import { MutableFunctionCall } from "./contractCall/mutableFunctionCall";

const provider = ethers.providers;

Expand Down Expand Up @@ -86,17 +93,16 @@ const getSelectedProvider = (context: vscode.ExtensionContext) => {

// Contract function calls
const displayBalance = async (context: vscode.ExtensionContext) => {

if(getSelectedNetwork(context) === undefined) {
if (getSelectedNetwork(context) === undefined) {
logger.log("No network selected. Please select a network.");
return;
}

const address: any = await context.workspaceState.get("account");
const nativeCurrencySymbol =
getSelectedNetConf(context).nativeCurrency.symbol;
try {

try {
getSelectedProvider(context)
.getBalance(address)
.then(async (value) => {
Expand Down Expand Up @@ -153,80 +159,47 @@ const callContractMethod = async (context: vscode.ExtensionContext) => {
"contract"
)) as CompiledJSONOutput;

if (compiledOutput == undefined) throw errors.ContractNotSelected;
if (compiledOutput === undefined) throw errors.ContractNotSelected;

const abi = getAbi(compiledOutput);
if (abi == undefined) throw new Error("Abi is not defined.");
if (abi === undefined) throw new Error("Abi is not defined.");

const abiItem = await getFunctionInputs(context);
if (abiItem === undefined) throw new Error("Function is not defined.");

const params_ = abiItem.inputs?.map((e: any) => e.value);
const params = params_ === undefined ? [] : params_;

logger.success(`Calling ${compiledOutput.name} : ${abiItem.name} -->`);

const contractAddres = getDeployedInputs(context).address;
if (contractAddres === undefined)
throw new Error("Enter deployed address of selected contract.");

if (abiItem.stateMutability === "view") {
selectContract(context);

const contract = new ethers.Contract(
contractAddres,
abi,
getSelectedProvider(context)
);

const result = await contract[abiItem.name as string](...params);
logger.success(
`Calling ${compiledOutput.name} : ${abiItem.name} --> Success!`
);
logger.log(JSON.stringify(result));
} else {
const contract = await getSignedContract(context, contractAddres);

let result;

if (abiItem.stateMutability === "nonpayable") {
const gasCondition = (await context.workspaceState.get(
"gas"
)) as string;

const gasEstimate = await getGasEstimates(gasCondition, context);
const settingsGasLimit = (await getConfiguration().get(
"gasLimit"
)) as number;
if (gasEstimate !== undefined) {
const maxFeePerGas = (gasEstimate as EstimateGas).price;
result = await contract[abiItem.name as string](...params, {
gasPrice: ethers.utils.parseUnits(maxFeePerGas.toString(), "gwei"),
gasLimit: settingsGasLimit,
});
} else {
result = await contract[abiItem.name as string](...params);
}
} else {
const found: any = abiItem.inputs?.find(
(e: any) => e.type === "uint256"
);
result = await contract[abiItem.name as string](...params, {
value: found.value,
});
}
const params_ = abiItem.inputs?.map((e: any) => e.value);
Copy link
Member

Choose a reason for hiding this comment

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

I think the params should be read inside the mutable & immutable call functions as now the params will contain the payable value.

const params = params_ === undefined ? [] : params_;

logger.success("Waiting for confirmation...");
const useContract: useContractType = {
Copy link
Member

Choose a reason for hiding this comment

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

Do not use variable name useContract as this name is generally used as a hook.

contractName: compiledOutput.name as string,
abiItemName: abiItem.name as string,
contractAddress: contractAddres,
};

logger.success(
`Calling ${useContract.contractName} : ${useContract.abiItemName} -->`
);

if (
abiItem.stateMutability === "view" ||
abiItem.stateMutability === "pure"
) {
await ImmutableFunctionCall(context, params, abi, useContract);
}

await result.wait();
logger.success("Transaction confirmed!");
logger.success(
`Calling ${compiledOutput.name} : ${abiItem.name} --> Success!`
);
logger.success(
`You can see detail of this transaction here. ${
getSelectedNetConf(context).blockScanner
}/tx/${result.hash}`
if (
abiItem.stateMutability === "nonpayable" ||
abiItem.stateMutability === "payable"
) {
await MutableFunctionCall(
context,
abiItem.stateMutability,
params,
useContract
);
}
} catch (err: any) {
Expand Down Expand Up @@ -337,6 +310,26 @@ const getContractFactoryWithParams = async (
return myContract;
};

const setPayableValue = async (context: vscode.ExtensionContext) => {
try {
const inputBoxOpts: InputBoxOptions = {
placeHolder: "value",
ignoreFocusOut: true,
};
const value = await window.showInputBox(inputBoxOpts);
if (value === undefined) {
return;
}
const valueInWei = ethers.utils.parseEther(value);
const nativeCurrencySymbol =
getSelectedNetConf(context).nativeCurrency.symbol;
await context.workspaceState.update("payableValue", valueInWei.toString());
logger.log(`payable value set to: ${value} ${nativeCurrencySymbol}`);
} catch (error) {
logger.log("Error: payable value is not saved");
}
};

export {
getConfiguration,
getNetworkNames,
Expand All @@ -349,4 +342,6 @@ export {
deployContract,
isTestingNetwork,
setTransactionGas,
setPayableValue,
getSignedContract,
};