Calling Contract Functions

Retrieve information or execute transactions on the blockchain

State Getters

State-getters are easily called and require no arguments. For the ComptrollerLib, you can reference what queries are available here.

const denominationAsset = await vaultComptroller.getDenominationAsset()
// ==> '0x9ske9EJ...'

State Changing Functions

Contract function calls that change state on Ethereum can be thought of as two steps:

  1. Passing arguments to the function to instantiate a transaction object

  2. Doing Ethereum things with that object

// Instantiate the transaction object
const transaction = InstantiatedClass.functionName.args(arg, arg, arg...) 

// Optional: `call` confirms that the transaction as constructed
// will execute succesfully. If so, it returns some details of the transaction.
// If not, it will throw an exception.
await transaction.call();

// Optional: get the gas limit the transaction will require (returns a BigNumber,
// and it's often useful to overestimate to prevent the transaction from failing).
const gasLimit = (await transaction.estimate()).mul(10).div(9);

// Optional: check somewhere to see what gas is currently going for
const gasPrice = yourGasPriceFetchingFunction();

// The .gas function call below is optional, and allows you to tweak the gas
// settings and execute the transaction. 
const txReceipt = await transaction.gas(gasLimit, gasPrice).send();
console.log('Pending transaction:', txReceipt.transactionHash);
console.log('The transaction was included in block number:', txReceipt.blockNumber);

Preparing Arguments: Helper Functions

Extension contract class methods require their arguments to be encoded into formatted strings to be interpreted by the smart contracts. The Enzyme SDK ships with helper functions that, when passed the correct parameters, output the appropriate string. TypeScript will describe the arguments that must be passed to your class' helper functions. These arguments are named semantically and should be easily interpreted. You can browse these utility functions (and some others) here, and see one in use here and here.

For example, to trade into or out of a cToken on Compound via Enzyme, you need to prepare three parameters: cToken, which is the address of the cToken in question, minIncomingAssetAmount and outgoingAssetAmount which are self-explanatory. These parameters then must be passed to an compoundArgs function which encodes them in the appropriate way. The value returned from that function is then passed to the contract method to execute the trade.

const encodedCallArgs = compoundArgs({
    cToken,
    minIncomingAssetAmount,
    outgoingAssetAmount
});

These methods will be used frequently in the examples to follow.

Call On Extension

Extensions are contracts that enable Vault configuration and interaction in a modular fashion. There are currently three types of extensions - fees, policies, and integration adapters.

All integration adapters must be called via a vault's Comptroller:

  1. Assemble and encode integration adapter contract-specific function arguments

  2. Assemble and encode integrationManager-specific arguments

  3. Pass encoded arguments to ComptrollerProxy.callOnExtension.args() along with three integration-specific arguments:

  • The IntegrationManager contract's address. This address is specific to each release of the protocol and can be found here.

  • A selector that defines the function being called on the integration adapter. These selectors can be found here. The correct selector should be imported from the SDK.

  • A member of the IntegrationManagerActionId enum, most usually .CallOnIntegration. The enum should be imported from the SDK.

import {  
		callOnIntegrationArgs, 
		uniswapV2TakeOrderArgs, 
		IntegrationManagerActionId,
		takeOrderSelector 
} from '@enzymefinance/protocol';

// the address of the adapter for the extension you wish to use
const uniswapV2Adapter = '0x23sdfsn...'; // for example only

// Step 1: assemble the arguments for the integration adapter
const takeOrderArgs = uniswapV2TakeOrderArgs({
      path: aPath, // for example only
      minIncomingAssetAmount: aMinIncomingAssetAmount, // for example only
      outgoingAssetAmount: anOutgoingAssetAmount, // for example only
});

// Step 2: assemble and encode the arguments for callOnExtension()
const callArgs = callOnIntegrationArgs({
      adapter,
      selector: takeOrderSelector,
      encodedCallArgs: takeOrderArgs,
});

// the address of the protocol release's integration manager contract
const integrationManagerAddress = '0x32sdf...' // for example only

// Step 3: Call the callOnExtension function with the assembled and encoded args
const transaction = vaultComptroller.callOnExtension.args(
		integrationManagerAddress, 
		IntegrationManagerActionId.CallOnIntegration, 
		callArgs
);

From there, your transaction is ready to verify, estimate, and send.

Last updated