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
- Contract Structure - Contract organization
- Attributes - Function attributes
- Interfaces - Interface definitions
- ursus-patterns repository - Inheritance examples