Skip to main content

Contract interface

When interacting with smart contracts in Unity, the IContract interface defines an interface of a given contract. This interface includes all the functions of the contract, and an optional constructor function and Bytecode field.

You can use the contract code generator to generate a contract interface given a contract ABI or Hardhat artifact JSON file.

To manually create a new contract interface, define a new interface that inherits from IContract. Optionally, declare the BackedType attribute.

#if UNITY_EDITOR || !ENABLE_MONO
[BackedType(typeof(ERC20Backing))]
#endif
public interface ERC20 : IContract
{
// Declare functions.
}

Declare contract functions

To declare a view or pure function of the contract, first set the return type, the function name (may differ from the actual name), and all parameters the function takes in a Task:

#if UNITY_EDITOR || !ENABLE_MONO
[BackedType(typeof(ERC20Backing))]
#endif
public interface ERC20 : IContract
{
Task<BigInteger> BalanceOf(EvmAddress account);
}

Once you have the function written, simply add the EvmMethodInfo at the top of the function to declare the metadata about the contract function. This includes the Name and whether it's a View function:

#if UNITY_EDITOR || !ENABLE_MONO
[BackedType(typeof(ERC20Backing))]
#endif
public interface ERC20 : IContract
{
[EvmMethodInfo(Name = "balanceOf", View = true)]
Task<BigInteger> BalanceOf(EvmAddress account);
}

To define the EVM type for a parameter, you can use the EvmParameterInfo attribute. However, this usually isn't needed, because the Contract class automatically infers most common types, such as EvmAddress to be address and string to be string.

#if UNITY_EDITOR || !ENABLE_MONO
[BackedType(typeof(ERC20Backing))]
#endif
public interface ERC20 : IContract
{
[EvmMethodInfo(Name = "balanceOf", View = true)]
Task<BigInteger> BalanceOf([EvmParameterInfo(Type = "address")] string account);
}

To define the EVM return type for the function, you can use EvmParamterInfo on the return type:

#if UNITY_EDITOR || !ENABLE_MONO
[BackedType(typeof(ERC20Backing))]
#endif
public interface ERC20 : IContract
{
[EvmMethodInfo(Name = "balanceOf", View = true)]
Task<BigInteger> BalanceOf(EvmAddress account);

[EvmMethodInfo(Name = "decimals", View = true)]
[return: EvmParameterInfo(Type = "uint8")]
Task<BigInteger> Decimals();
}

Use the Task return type

We recommend always using Task as the return type when declaring contract functions, even if the given provider does not use Task. When you don't use Task, the Contract class is blocked until a response from the given Provider is received. This means if the Provider returns a Task of the request, the Contract class is blocked until that Task completes, which may lead to a deadlock.

Define a contract constructor

To define a constructor function, use the EvmConstructorMethod attribute at the top of the function declaring the constructor. The return type of the function must be the type of the interface, since a new instance of the interface is returned by the Contract class.

Also, do one of the following:

  • Declare a static readonly string Bytecode in the interface that has the bytecode.

    #if UNITY_EDITOR || !ENABLE_MONO
    [BackedType(typeof(ERC20Backing))]
    #endif
    public interface ERC20 : IContract
    {
    public static readonly string Bytecode = "0x6080604052348015620000115760008....";

    [EvmConstructorMethod]
    Task<ERC20> DeployNew(String name_, String symbol_);

    [EvmMethodInfo(Name = "balanceOf", View = true)]
    Task<BigInteger> BalanceOf(EvmAddress account);

    [EvmMethodInfo(Name = "decimals", View = true)]
    [return: EvmParameterInfo(Type = "uint8")]
    Task<BigInteger> Decimals();
    }
  • Set the Bytecode field in the EvmConstructorMethod attribute.

    #if UNITY_EDITOR || !ENABLE_MONO
    [BackedType(typeof(ERC20Backing))]
    #endif
    public interface ERC20 : IContract
    {
    [EvmConstructorMethod(Bytecode = "0x608060405238....")]
    Task<ERC20> DeployNew(String name_, String symbol_);

    [EvmMethodInfo(Name = "balanceOf", View = true)]
    Task<BigInteger> BalanceOf(EvmAddress account);

    [EvmMethodInfo(Name = "decimals", View = true)]
    [return: EvmParameterInfo(Type = "uint8")]
    Task<BigInteger> Decimals();
    }

By default, the contract code generator uses the second option.