Contracts
A contract is a collection of type definitions, data (its state), and code (its functions), that is stored in the contract storage area of an account.
Contracts are where all composite types interfaces for these types have to be defined. Therefore, an object of one of these types cannot exist without having been defined in a deployed Cadence contract.
Contracts can be deployed to accounts, updated, and removed from accounts
using the contracts
object of authorized accounts.
See the account contracts page
for more information about these operations.
Contracts are types. They are similar to composite types, but are stored differently than structs or resources and cannot be used as values, copied, or moved like resources or structs.
Contracts stay in an account's contract storage area and can only be added, updated, or removed by the account owner with special commands.
Contracts are declared using the contract
keyword.
The keyword is followed by the name of the contract.
_10access(all)_10contract SomeContract {_10 // ..._10}
Contracts cannot be nested in each other.
_10access(all)_10contract Invalid {_10_10 // Invalid: Contracts cannot be nested in any other type._10 //_10 access(all)_10 contract Nested {_10 // ..._10 }_10}
One of the simplest forms of a contract would just be one with a state field, a function, and an initializer that initializes the field:
_20access(all)_20contract HelloWorld {_20_20 // Declare a stored state field in HelloWorld_20 //_20 access(all)_20 let greeting: String_20_20 // Declare a function that can be called by anyone_20 // who imports the contract_20 //_20 access(all)_20 fun hello(): String {_20 return self.greeting_20 }_20_20 init() {_20 self.greeting = "Hello World!"_20 }_20}
Transactions and other contracts can interact with contracts by importing them at the beginning of a transaction or contract definition.
Anyone could call the above contract's hello
function by importing
the contract from the account it was deployed to and using the imported
object to call the hello function.
_18import HelloWorld from 0x42_18_18// Invalid: The contract does not know where hello comes from_18//_18log(hello()) // Error_18_18// Valid: Using the imported contract object to call the hello_18// function_18//_18log(HelloWorld.hello()) // prints "Hello World!"_18_18// Valid: Using the imported contract object to read the greeting_18// field._18log(HelloWorld.greeting) // prints "Hello World!"_18_18// Invalid: Cannot call the init function after the contract has been created._18//_18HelloWorld.init() // Error
There can be any number of contracts per account and they can include an arbitrary amount of data. This means that a contract can have any number of fields, functions, and type definitions, but they have to be in the contract and not another top-level definition.
_10// Invalid: Top-level declarations are restricted to only be contracts_10// or contract interfaces. Therefore, all of these would be invalid_10// if they were deployed to the account contract storage and_10// the deployment would be rejected._10//_10access(all) resource Vault {}_10access(all) struct Hat {}_10access(all) fun helloWorld(): String {}_10let num: Int
Another important feature of contracts is that instances of resources and events that are declared in contracts can only be created/emitted within functions or types that are declared in the same contract.
It is not possible create instances of resources and events outside the contract.
The contract below defines a resource interface Receiver
and a resource Vault
that implements that interface. The way this example is written,
there is no way to create this resource, so it would not be usable.
_44// Valid_44access(all) contract FungibleToken {_44_44 access(all) resource interface Receiver {_44_44 access(all) balance: Int_44_44 access(all) fun deposit(from: @{Receiver}) {_44 pre {_44 from.balance > 0:_44 "Deposit balance needs to be positive!"_44 }_44 post {_44 self.balance == before(self.balance) + before(from.balance):_44 "Incorrect amount removed"_44 }_44 }_44 }_44_44 access(all) resource Vault: Receiver {_44_44 // keeps track of the total balance of the accounts tokens_44 access(all) var balance: Int_44_44 init(balance: Int) {_44 self.balance = balance_44 }_44_44 // withdraw subtracts amount from the vaults balance and_44 // returns a vault object with the subtracted balance_44 access(all) fun withdraw(amount: Int): @Vault {_44 self.balance = self.balance - amount_44 return <-create Vault(balance: amount)_44 }_44_44 // deposit takes a vault object as a parameter and adds_44 // its balance to the balance of the Account's vault, then_44 // destroys the sent vault because its balance has been consumed_44 access(all) fun deposit(from: @{Receiver}) {_44 self.balance = self.balance + from.balance_44 destroy from_44 }_44 }_44}
If a user tried to run a transaction that created an instance of the Vault
type,
the type checker would not allow it because only code in the FungibleToken
contract can create new Vault
s.
_10import FungibleToken from 0x42_10_10// Invalid: Cannot create an instance of the `Vault` type outside_10// of the contract that defines `Vault`_10//_10let newVault <- create FungibleToken.Vault(balance: 10)
Account access
Contracts can access the account they are deployed to:
contracts have the implicit field named account
which is only accessible within the contract.
_10let account: auth(Storage, Keys, Contracts, Inbox, Capabilities) &Account`,
The account reference is fully entitled, so grants access to the account's storage, keys, contracts, etc.
For example, this gives the contract the ability to write to the account's storage when the contract is initialized.
_10init(balance: Int) {_10 self.account.storage.save(_10 <-create Vault(balance: 1000),_10 to: /storage/initialVault_10 )_10}
Contract interfaces
Like composite types, contracts can have interfaces that specify rules about their behavior, their types, and the behavior of their types.
Contract interfaces have to be declared globally. Declarations cannot be nested in other types.
Contract interfaces may not declare concrete types (other than events), but they can declare interfaces.
If a contract interface declares an interface type, the implementing contract
does not have to also define that interface.
They can refer to that nested interface by saying {ContractInterfaceName}.{NestedInterfaceName}
_26// Declare a contract interface that declares an interface and a resource_26// that needs to implement that interface in the contract implementation._26//_26access(all) contract interface InterfaceExample {_26_26 // Implementations do not need to declare this_26 // They refer to it as InterfaceExample.NestedInterface_26 //_26 access(all) resource interface NestedInterface {}_26_26 // Implementations must declare this type_26 //_26 access(all) resource Composite: NestedInterface {}_26}_26_26access(all) contract ExampleContract: InterfaceExample {_26_26 // The contract doesn't need to redeclare the `NestedInterface` interface_26 // because it is already declared in the contract interface_26_26 // The resource has to refer to the resource interface using the name_26 // of the contract interface to access it_26 //_26 access(all) resource Composite: InterfaceExample.NestedInterface {_26 }_26}