Skip to main content

Contract Class Structure

Restrictions for Simplified Code Checks

Inheritance Rules

  • Only one inheritance is allowed from ContractBase, generated by the contract plugin as a nested type in ContractContainer.
  • Only one inheritance is allowed from CSharpSmartContract.
  • Only one inheritance is allowed from ContractState.
  • The type inherited from ContractState should be the element type of the CSharpSmartContract generic instance type.

If these rules are not followed, code deployment will fail.

Contract Class Structure

Field Usage Limitations

In Contract Implementation Class

  • Initial values for non-readonly, non-constant fields are not allowed (for both static and non-static fields). This is because their value will reset to 0 or null after the first execution, losing the initial value.

Allowed:

class MyContract : MyContractBase
{
int test;
static const int test = 2;
}

Not Allowed:

class MyContract : MyContractBase
{
! int test = 2;
}
class MyContract : MyContractBase
{
int test;

public MyContract
{
! test = 2;
}
}
  • Only primitive types or the following types are allowed for readonly/constant fields:
    • Marshaller<T>
    • Method<T, T>
    • MessageParser<T>
    • FieldCodec<T>
    • MapField<T, T>
    • ReadonlyCollection<T>
    • ReadonlyDictionary<T, T> Note: T can only be a primitive type.

In Non-Contract Classes (Classes not inheriting from ContractBase<T>)

  • Initial values for non-readonly, non-constant static fields are not allowed. They reset to 0 or null after the first execution, losing the initial value.

Allowed:

class AnyClass
{
static int test;
}

Not Allowed:

class AnyClass
{
! static int test = 2;
}
class AnyClass
{
static int test;

public AnyClass
{
! test = 2;
}
}
  • Exception: Fields with FileDescriptor types are allowed due to protobuf-generated code. These fields don’t have a readonly modifier and write access to them is allowed only from the constructor of the declaring type.

Allowed:

public class TestType
{
private static FileDescriptor test;

public class TestType
{
test = ...
}
}

Not Allowed:

public class TestType
{
private static FileDescriptor test;

public TestType
{
test = ...
}

! public void SetFromSomeWhereElse(FileDescriptor input)
! {
! test = input;
! }
}
  • Accessing test fields is restricted to the declaring type’s constructor only.

  • Only the following types are allowed for readonly/constant static fields:

    • Marshaller<T>
    • Method<T, T>
    • MessageParser<T>
    • FieldCodec<T>
    • MapField<T, T>
    • ReadonlyCollection<T>
    • ReadonlyDictionary<T, T>

Note: T can only be a primitive type.

  • Exception: If a type has a readonly field of the same type as itself, it is only allowed if the type has no instance fields (to support LINQ-related generated types).

Allowed:

public class TestType
{
private static readonly TestType test;

private static int i;
}

Not Allowed:

public class TestType
{
private static readonly TestType test;

! private int i;
}

In Contract State

In contract state, only the following types are allowed:

Primitive Types

  • BoolState
  • Int32State
  • UInt32State
  • Int64State
  • UInt64State
  • StringState
  • BytesState
  • Complex Types

Complex Types

  • SingletonState<T>
  • ReadonlyState<T>
  • MappedState<T, T>
  • MappedState<T, T, T>
  • MappedState<T, T, T, T>
  • MappedState<T, T, T, T, T>
  • MethodReference<T, T>
  • ProtobufState<T>
  • ContractReferenceState