Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contract Inheritance

Ursus supports contract inheritance, allowing you to build modular, reusable contract components. This enables code reuse, abstraction, and hierarchical contract design.

Basic Inheritance

Declaring Parent Contracts

Syntax:

Contract ChildContract ;
Inherits ParentContract ;

Example:

(* Parent contract *)
Contract Ownable ;
Sends To ;
Inherits ;

Record Contract := {
    owner: address
}.

Ursus Definition onlyOwner: UExpression unit true.
{
    ::// require_(msg->sender == owner, "Not owner") |.
}
return.
Defined.

(* Child contract *)
Contract MyToken ;
Sends To ;
Inherits Ownable ;

Record Contract := {
    totalSupply: uint256;
    balances: mapping address uint256
}.

Multiple Inheritance

Inherit from multiple parent contracts:

Syntax:

Inherits Parent1 Parent2 Parent3 ;

Example:

Contract MyContract ;
Sends To ;
Inherits Ownable Pausable ReentrancyGuard ;

Inheritance Modes

Full Inheritance

Inherit all functions and state:

Inherits ParentContract ;

Selective Inheritance

Inherit specific functions:

Inherits ParentContract => overrides [function1 function2] ;

Example:

Contract CustomToken ;
Inherits ERC20 => overrides [transfer transferFrom] ;

Overriding Functions

Basic Override

Parent:

Contract Base ;
Sends To ;
Inherits ;

#[virtual]
Ursus Definition getValue: UExpression uint256 false.
{
    ::// _result := {42} |.
}
return.
Defined.

Child:

Contract Derived ;
Inherits Base ;

#[override]
Ursus Definition getValue: UExpression uint256 false.
{
    ::// _result := {100} |.
}
return.
Defined.

Calling Parent Implementation

Use super to call parent function:

#[override]
Ursus Definition transfer (to: address) (amount: uint256):
  UExpression boolean false.
{
    ::// super->transfer(to, amount) .
    ::// emit CustomTransfer(msg->sender, to, amount) |.
}
return @true.
Defined.

State Inheritance

Merging State

Child contract state includes parent state:

Parent:

Contract Ownable ;
Record Contract := {
    owner: address
}.

Child:

Contract MyToken ;
Inherits Ownable ;

Record Contract := {
    (* Inherited: owner: address *)
    totalSupply: uint256;
    balances: mapping address uint256
}.

Accessing Parent State

Access parent state fields directly:

Ursus Definition checkOwner: UExpression boolean false.
{
    ::// _result := (msg->sender == owner) |.  (* owner from Ownable *)
}
return.
Defined.

Abstract Contracts

Defining Abstract Contracts

Contracts with unimplemented functions:

Contract AbstractToken ;
Sends To ;
Inherits ;

(* Abstract function *)
#[no_body, virtual]
Ursus Definition _beforeTransfer (from to: address) (amount: uint256):
  UExpression unit false.

(* Concrete function *)
Ursus Definition transfer (to: address) (amount: uint256):
  UExpression boolean false.
{
    ::// _beforeTransfer(msg->sender, to, amount) .
    ::// balances[[msg->sender]] := balances[[msg->sender]] - amount .
    ::// balances[[to]] := balances[[to]] + amount |.
}
return @true.
Defined.

Implementing Abstract Functions

Contract ConcreteToken ;
Inherits AbstractToken ;

#[override]
Ursus Definition _beforeTransfer (from to: address) (amount: uint256):
  UExpression unit false.
{
    ::// require_(amount > {0}, "Zero amount") |.
}
return.
Defined.

Interfaces

Defining Interfaces

Contracts with only function signatures:

Contract IERC20 ;
Sends To ;
Inherits ;

#[no_body]
Ursus Definition transfer (to: address) (amount: uint256):
  UExpression boolean false.

#[no_body]
Ursus Definition balanceOf (account: address):
  UExpression uint256 false.

#[no_body]
Ursus Definition approve (spender: address) (amount: uint256):
  UExpression boolean false.

Implementing Interfaces

Contract ERC20 ;
Inherits IERC20 ;

#[override]
Ursus Definition transfer (to: address) (amount: uint256):
  UExpression boolean false.
{
    (* Implementation *)
}
return @true.
Defined.

#[override]
Ursus Definition balanceOf (account: address):
  UExpression uint256 false.
{
    ::// _result := balances[[account]] |.
}
return.
Defined.

Inheritance Patterns

Pattern 1: Ownable

Contract Ownable ;
Sends To ;
Inherits ;

Record Contract := {
    owner: address
}.

Ursus Definition constructor: UExpression unit false.
{
    ::// owner := msg->sender |.
}
return.
Defined.

Ursus Definition onlyOwner: UExpression unit true.
{
    ::// require_(msg->sender == owner, "Not owner") |.
}
return.
Defined.

Ursus Definition transferOwnership (newOwner: address): UExpression unit true.
{
    ::// onlyOwner() .
    ::// owner := newOwner |.
}
return.
Defined.

Pattern 2: Pausable

Contract Pausable ;
Inherits Ownable ;

Record Contract := {
    paused: boolean
}.

Ursus Definition whenNotPaused: UExpression unit true.
{
    ::// require_(!paused, "Paused") |.
}
return.
Defined.

Ursus Definition pause: UExpression unit true.
{
    ::// onlyOwner() .
    ::// paused := @true |.
}
return.
Defined.

Ursus Definition unpause: UExpression unit true.
{
    ::// onlyOwner() .
    ::// paused := @false |.
}
return.
Defined.

Pattern 3: ReentrancyGuard

Contract ReentrancyGuard ;
Sends To ;
Inherits ;

Record Contract := {
    locked: boolean
}.

Ursus Definition nonReentrant: UExpression unit true.
{
    ::// require_(!locked, "Reentrant call") .
    ::// locked := @true |.
}
return.
Defined.

Ursus Definition unlock: UExpression unit false.
{
    ::// locked := @false |.
}
return.
Defined.

Diamond Inheritance

Problem

Multiple inheritance can cause ambiguity:

Contract A ;
Ursus Definition foo: UExpression uint256 false.

Contract B ;
Inherits A ;

Contract C ;
Inherits A ;

Contract D ;
Inherits B C ;  (* Which foo? *)

Solution: Linearization

Ursus uses C3 linearization to resolve:

D -> B -> C -> A

Override explicitly:

Contract D ;
Inherits B C ;

#[override]
Ursus Definition foo: UExpression uint256 false.
{
    ::// _result := B->foo() |.  (* Explicitly choose B's implementation *)
}
return.
Defined.

See Also