// Operations for managing smart contracts
import { z } from "zod";
import { getContractsApi } from "../common/client";
import {
CurvegridResourceNotFoundError,
CurvegridError,
} from "../common/errors";
// Schema for listing contracts
export const ListContractsSchema = z.object({
limit: z
.number()
.optional()
.describe("Maximum number of contracts to return"),
offset: z.number().optional().describe("Offset for pagination"),
});
// Schema for getting contract details
export const GetContractSchema = z.object({
address: z.string().describe("Contract address"),
});
// Schema for calling a contract method
export const CallContractMethodSchema = z.object({
aliasOrRawAddress: z.string().describe("Address or Alias"),
contractLabel: z.string().describe("Contract Label"),
method: z.string().describe("Method name to call"),
args: z.array(z.any()).optional().describe("Arguments for the method call"),
});
// Schema for sending a contract transaction
export const SendContractTransactionSchema = z.object({
address: z.string().describe("Contract address"),
method: z.string().describe("Method name to call"),
args: z.array(z.any()).optional().describe("Arguments for the method call"),
signer: z.string().describe("Address of the signer to use"),
gasLimit: z
.number()
.optional()
.describe('Gas limit for the transaction (will be passed as "gas" to SDK)'),
value: z
.string()
.optional()
.describe("Value in wei to send with the transaction"),
});
// List all contracts
export async function listContracts(
options: z.infer<typeof ListContractsSchema> = {},
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.listContracts({
// Pass options to the SDK
// The SDK's listContracts method accepts RawAxiosRequestConfig as options
// We can pass our options through the params property
params: {
limit: options.limit,
offset: options.offset,
},
});
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError("Contracts not found");
}
throw new CurvegridError(`Failed to list contracts: ${error.message}`);
}
}
// Get contract details
export async function getContract(options: z.infer<typeof GetContractSchema>) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.getContract(options.address);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract at address ${options.address} not found`,
);
}
throw new CurvegridError(`Failed to get contract: ${error.message}`);
}
}
// Call a contract method (read-only)
export async function callContractMethod(
options: z.infer<typeof CallContractMethodSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.callContractFunction(
options.aliasOrRawAddress,
options.contractLabel, // contract name is same as address
options.method,
{
args: options.args || [],
signAndSubmit: false,
nonceManagement: true,
formatInts: "auto",
},
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract at address ${options.aliasOrRawAddress} or method ${options.method} not found`,
);
}
throw new CurvegridError(
`Failed to call contract method: ${error.message}`,
);
}
}
// Send a contract transaction (write)
export async function sendContractTransaction(
options: z.infer<typeof SendContractTransactionSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.callContractFunction(
options.address,
options.address, // contract name is same as address
options.method,
{
args: options.args || [],
from: options.signer,
gas: options.gasLimit, // SDK uses 'gas' instead of 'gasLimit'
value: options.value,
signAndSubmit: true,
nonceManagement: true,
formatInts: "auto",
},
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract at address ${options.address} or method ${options.method} not found`,
);
}
throw new CurvegridError(
`Failed to send contract transaction: ${error.message}`,
);
}
}
// Schema for linking an address to a contract
export const LinkAddressContractSchema = z.object({
instanceAlias: z
.string()
.describe("An alias for the deployed contract address"),
contractLabel: z.string().describe("Contract Label to link to"),
version: z.string().optional().describe("The contract version"),
startingBlock: z
.string()
.optional()
.describe(
'The block number from which to start syncing events. Can be "latest", an absolute block number, or a relative block number (e.g., "-100")',
),
});
// Link an address to a contract
export async function linkAddressContract(
options: z.infer<typeof LinkAddressContractSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.linkAddressContract(
options.instanceAlias,
{
label: options.contractLabel,
version: options.version,
startingBlock: options.startingBlock,
},
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Address ${options.instanceAlias} or contract ${options.contractLabel} not found`,
);
}
throw new CurvegridError(
`Failed to link address to contract: ${error.message}`,
);
}
}
// Schema for unlinking an address from a contract
export const UnlinkAddressContractSchema = z.object({
addressOrAlias: z.string().describe("An address or the alias of an address"),
contract: z.string().describe("Contract name"),
});
// Unlink an address from a contract
export async function unlinkAddressContract(
options: z.infer<typeof UnlinkAddressContractSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.unlinkAddressContract(
options.addressOrAlias,
options.contract,
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Address ${options.addressOrAlias} or contract ${options.contract} not found`,
);
}
throw new CurvegridError(
`Failed to unlink address from contract: ${error.message}`,
);
}
}
// Schema for getting event indexing status
export const GetEventIndexingStatusSchema = z.object({
addressOrAlias: z.string(),
contract: z.string(),
});
// Get event indexing status
export async function getEventIndexingStatus(
options: z.infer<typeof GetEventIndexingStatusSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.getEventIndexingStatus(
options.addressOrAlias,
options.contract,
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Address ${options.addressOrAlias} or contract ${options.contract} not found`,
);
}
throw new CurvegridError(
`Failed to get event indexing status: ${error.message}`,
);
}
}
// Schema for creating a contract
export const CreateContractSchema = z.object({
contract: z.string().describe("Contract name"),
label: z.string().describe("Contract label"),
contractName: z.string().describe("The name of the contract"),
version: z.string().describe("The contract version"),
bin: z.string().optional(),
rawAbi: z.string(),
userDoc: z.string().optional(),
developerDoc: z.string().optional(),
metadata: z.string().optional(),
isFavorite: z.boolean().optional(),
});
// Create a contract
export async function createContract(
contract: z.infer<typeof CreateContractSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.createContract(contract.label, {
...contract,
});
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Failed to create contract ${contract}`,
);
}
throw new CurvegridError(`Failed to create contract: ${error.message}`);
}
}
// Schema for creating multiple contracts
export const CreateContractsSchema = z.object({
contracts: z
.array(
z.object({
label: z.string().describe("Contract label"),
contractName: z.string().describe("The name of the contract"),
version: z.string().describe("The contract version"),
bin: z.string().optional(),
rawAbi: z.string(),
userDoc: z.string().optional(),
developerDoc: z.string().optional(),
metadata: z.string().optional(),
isFavorite: z.boolean().optional(),
}),
)
.describe("Array of contracts to create"),
});
// Schema for deleting a contract
export const DeleteContractSchema = z.object({
contract: z.string().describe("Contract name"),
});
// Delete a contract
export async function deleteContract(
options: z.infer<typeof DeleteContractSchema>,
) {
try {
const contractsApi = getContractsApi();
await contractsApi.deleteContract(options.contract);
return;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} not found`,
);
}
throw new CurvegridError(`Failed to delete contract: ${error.message}`);
}
}
// Schema for deploying a contract
export const DeployContractSchema = z.object({
contract: z.string().describe("Contract name"),
args: z.array(z.any()).optional().describe("Constructor arguments"),
from: z.string().describe("Address of the signer to use"),
signature: z.string().optional().describe("Function signature"),
nonce: z.number().optional(),
gasPrice: z.number().optional(),
gasFeeCap: z.number().optional(),
gasTipCap: z.number().optional(),
gas: z.number().optional(),
to: z.string().optional().describe("Destination address"),
value: z
.string()
.optional()
.describe("Amount (in wei) to send with the transaction"),
signAndSubmit: z
.boolean()
.optional()
.default(true)
.describe(
"If the `from` address is an HSM address and this flag is set to `true`, the transaction will be automatically signed and submitted to the blockchain",
),
nonceManagement: z.boolean().optional().default(true),
preEIP1559: z.boolean().optional(),
signer: z
.string()
.optional()
.describe(
"The address to sign the transaction with (if different from `from`)",
),
formatInts: z.enum(["auto", "as_numbers", "as_strings"]).optional(),
contractOverride: z
.boolean()
.optional()
.describe(
"If set to true the given address and contract don't need to be linked for the function to be called",
),
});
// Deploy a contract
export async function deployContract(
options: z.infer<typeof DeployContractSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.deployContract(options.contract, {
args: options.args || [],
from: options.from,
signature: options.signature,
nonce: options.nonce,
gasPrice: options.gasPrice,
gasFeeCap: options.gasFeeCap,
gasTipCap: options.gasTipCap,
gas: options.gas,
to: options.to,
value: options.value,
signAndSubmit:
options.signAndSubmit !== undefined ? options.signAndSubmit : true,
nonceManagement:
options.nonceManagement !== undefined ? options.nonceManagement : true,
preEIP1559: options.preEIP1559,
signer: options.signer,
formatInts: options.formatInts || "auto",
contractOverride: options.contractOverride,
});
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} not found`,
);
}
throw new CurvegridError(`Failed to deploy contract: ${error.message}`);
}
}
// Schema for listing contract versions
export const ListContractVersionsSchema = z.object({
contract: z.string().describe("Contract name"),
});
// List contract versions
export async function listContractVersions(
options: z.infer<typeof ListContractVersionsSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.listContractVersions(options.contract);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} not found`,
);
}
throw new CurvegridError(
`Failed to list contract versions: ${error.message}`,
);
}
}
// Schema for getting contract versions
export const GetContractVersionsSchema = z.object({
contract: z.string().describe("Contract name"),
});
// Get contract versions
export async function getContractVersions(
options: z.infer<typeof GetContractVersionsSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.getContractVersions(options.contract);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} not found`,
);
}
throw new CurvegridError(
`Failed to get contract versions: ${error.message}`,
);
}
}
// Schema for getting a contract version
export const GetContractVersionSchema = z.object({
contract: z.string().describe("Contract name"),
version: z.string().describe("Contract version"),
});
// Get a contract version
export async function getContractVersion(
options: z.infer<typeof GetContractVersionSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.getContractVersion(
options.contract,
options.version,
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} version ${options.version} not found`,
);
}
throw new CurvegridError(
`Failed to get contract version: ${error.message}`,
);
}
}
// Schema for deleting a contract version
export const DeleteContractVersionSchema = z.object({
contract: z.string().describe("Contract name"),
version: z.string().describe("Contract version"),
});
// Delete a contract version
export async function deleteContractVersion(
options: z.infer<typeof DeleteContractVersionSchema>,
) {
try {
const contractsApi = getContractsApi();
await contractsApi.deleteContractVersion(options.contract, options.version);
return;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} version ${options.version} not found`,
);
}
throw new CurvegridError(
`Failed to delete contract version: ${error.message}`,
);
}
}
// Schema for deploying a contract version
export const DeployContractVersionSchema = z.object({
contract: z.string().describe("Contract name"),
version: z.string().describe("Contract version"),
args: z.array(z.any()).optional().describe("Constructor arguments"),
from: z.string().describe("Address of the signer to use"),
gas: z.number().optional(),
value: z
.string()
.optional()
.describe("Value in wei to send with the transaction"),
});
// Deploy a contract version
export async function deployContractVersion(
options: z.infer<typeof DeployContractVersionSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.deployContractVersion(
options.contract,
options.version,
{
args: options.args || [],
from: options.from,
gas: options.gas,
value: options.value,
signAndSubmit: true,
nonceManagement: true,
formatInts: "auto",
},
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} version ${options.version} not found`,
);
}
throw new CurvegridError(
`Failed to deploy contract version: ${error.message}`,
);
}
}
// Schema for getting function type conversions
export const GetFunctionTypeConversionsSchema = z.object({
contract: z.string(),
version: z.string(),
method: z.string(),
});
// Get function type conversions
export async function getFunctionTypeConversions(
options: z.infer<typeof GetFunctionTypeConversionsSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.getFunctionTypeConversions(
options.contract,
options.version,
options.method,
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} version ${options.version} method ${options.method} not found`,
);
}
throw new CurvegridError(
`Failed to get function type conversions: ${error.message}`,
);
}
}
// Schema for setting function type conversions
export const SetFunctionTypeConversionsSchema = z.object({
contract: z.string().describe("Contract name"),
version: z.string().describe("Contract version"),
method: z.string().describe("Contract function"),
options: z
.object({
inputs: z
.array(
z.object({
typeConversion: z.object({
mode: z.string().describe("The type conversion mode"),
decimalsAbsolute: z.number().nullable(),
decimalsFunction: z.string().nullable(),
}),
}),
)
.describe("Type conversions for event inputs"),
})
.describe("Type conversion options"),
outputs: z
.array(
z.object({
type: z.string().describe("The type conversion to apply"),
components: z
.array(
z.object({
type: z.string().describe("The type conversion to apply"),
}),
)
.optional()
.describe("For struct types, the type conversions for each field"),
}),
)
.describe("Type conversions for function outputs"),
});
// Set function type conversions
export async function setFunctionTypeConversions(
options: z.infer<typeof SetFunctionTypeConversionsSchema>,
) {
try {
const contractsApi = getContractsApi();
await contractsApi.setFunctionTypeConversions(
options.contract,
options.version,
options.method,
options.options,
);
return;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} version ${options.version} method ${options.method} not found`,
);
}
throw new CurvegridError(
`Failed to set function type conversions: ${error.message}`,
);
}
}
// Schema for getting event type conversions
export const GetEventTypeConversionsSchema = z.object({
contract: z.string().describe("Contract name"),
version: z.string().describe("Contract version"),
event: z.string().describe("Contract event"),
});
// Get event type conversions
export async function getEventTypeConversions(
options: z.infer<typeof GetEventTypeConversionsSchema>,
) {
try {
const contractsApi = getContractsApi();
const response = await contractsApi.getEventTypeConversions(
options.contract,
options.version,
options.event,
);
return response.data.result;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} version ${options.version} event ${options.event} not found`,
);
}
throw new CurvegridError(
`Failed to get event type conversions: ${error.message}`,
);
}
}
// Schema for setting event type conversions
export const SetEventTypeConversionsSchema = z.object({
contract: z.string().describe("Contract name"),
version: z.string().describe("Contract version"),
event: z.string().describe("Contract event"),
options: z
.object({
inputs: z
.array(
z.object({
typeConversion: z.object({
mode: z.string().describe("The type conversion mode"),
decimalsAbsolute: z.number().nullable(),
decimalsFunction: z.string().nullable(),
}),
}),
)
.describe("Type conversions for event inputs"),
})
.describe("Type conversion options"),
});
// Set event type conversions
export async function setEventTypeConversions(
options: z.infer<typeof SetEventTypeConversionsSchema>,
) {
try {
const contractsApi = getContractsApi();
await contractsApi.setEventTypeConversions(
options.contract,
options.version,
options.event,
options.options,
);
return;
} catch (error: any) {
if (error.response?.status === 404) {
throw new CurvegridResourceNotFoundError(
`Contract ${options.contract} version ${options.version} event ${options.event} not found`,
);
}
throw new CurvegridError(
`Failed to set event type conversions: ${error.message}`,
);
}
}