Contracts
Accounts store contracts. A contract can also just be an interface.
An account exposes its inbox through the contracts
field,
which has the type Account.Contracts
.
Account.Contracts
_71access(all)_71struct Contracts {_71_71 /// The names of all contracts deployed in the account._71 access(all)_71 let names: [String]_71_71 /// Returns the deployed contract for the contract/contract interface with the given name in the account, if any._71 ///_71 /// Returns nil if no contract/contract interface with the given name exists in the account._71 access(all)_71 view fun get(name: String): DeployedContract?_71_71 /// Returns a reference of the given type to the contract with the given name in the account, if any._71 ///_71 /// Returns nil if no contract with the given name exists in the account,_71 /// or if the contract does not conform to the given type._71 access(all)_71 view fun borrow<T: &Any>(name: String): T?_71_71 /// Adds the given contract to the account._71 ///_71 /// The `code` parameter is the UTF-8 encoded representation of the source code._71 /// The code must contain exactly one contract or contract interface,_71 /// which must have the same name as the `name` parameter._71 ///_71 /// All additional arguments that are given are passed further to the initializer_71 /// of the contract that is being deployed._71 ///_71 /// The function fails if a contract/contract interface with the given name already exists in the account,_71 /// if the given code does not declare exactly one contract or contract interface,_71 /// or if the given name does not match the name of the contract/contract interface declaration in the code._71 ///_71 /// Returns the deployed contract._71 access(Contracts | AddContract)_71 fun add(_71 name: String,_71 code: [UInt8]_71 ): DeployedContract_71_71 /// Updates the code for the contract/contract interface in the account._71 ///_71 /// The `code` parameter is the UTF-8 encoded representation of the source code._71 /// The code must contain exactly one contract or contract interface,_71 /// which must have the same name as the `name` parameter._71 ///_71 /// Does **not** run the initializer of the contract/contract interface again._71 /// The contract instance in the world state stays as is._71 ///_71 /// Fails if no contract/contract interface with the given name exists in the account,_71 /// if the given code does not declare exactly one contract or contract interface,_71 /// or if the given name does not match the name of the contract/contract interface declaration in the code._71 ///_71 /// Returns the deployed contract for the updated contract._71 access(Contracts | UpdateContract)_71 fun update(name: String, code: [UInt8]): DeployedContract_71_71 /// Removes the contract/contract interface from the account which has the given name, if any._71 ///_71 /// Returns the removed deployed contract, if any._71 ///_71 /// Returns nil if no contract/contract interface with the given name exists in the account._71 access(Contracts | RemoveContract)_71 fun remove(name: String): DeployedContract?_71}_71_71entitlement Contracts_71_71entitlement AddContract_71entitlement UpdateContract_71entitlement RemoveContract
Deployed contract
Accounts store "deployed contracts," that is, the code of the contract:
_28access(all)_28struct DeployedContract {_28 /// The address of the account where the contract is deployed at._28 access(all)_28 let address: Address_28_28 /// The name of the contract._28 access(all)_28 let name: String_28_28 /// The code of the contract._28 access(all)_28 let code: [UInt8]_28_28 /// Returns an array of `Type` objects representing all the public type declarations in this contract_28 /// (e.g. structs, resources, enums)._28 ///_28 /// For example, given a contract_28 /// ```_28 /// contract Foo {_28 /// access(all) struct Bar {...}_28 /// access(all) resource Qux {...}_28 /// }_28 /// ```_28 /// then `.publicTypes()` will return an array equivalent to the expression `[Type<Bar>(), Type<Qux>()]`_28 access(all)_28 fun publicTypes(): [Type]_28}
Note that this is type only provides information about a deployed contract, it is not the contract instance, the result of importing a contract.
Getting a deployed contract
The function contracts.get
retrieves a deployed contract:
_10access(all)_10view fun get(name: String): DeployedContract?
The function returns the deployed contract with the given name, if any.
If no contract with the given name exists in the account, the function returns nil
.
For example, assuming that an account has a contract named Test
deployed to it,
the contract can be retrieved as follows:
_10let account = getAccount(0x1)_10let contract = account.contracts.get(name: "Test")
Borrowing a deployed contract
Contracts can be "borrowed" to effectively perform a dynamic import dependent on a specific execution path.
This is in contrast to a typical import statement, for example import T from 0x1
,
which statically imports a contract.
The contracts.borrow
function obtains a reference to a contract instance:
_10access(all)_10view fun borrow<T: &Any>(name: String): T?
The functions returns a reference to the contract instance stored with that name on the account,
if it exists, and if it has the provided type T
.
If no contract with the given name exists in the account, the function returns nil
.
For example, assuming that a contract named Test
which conforms to the TestInterface
interface is deployed to an account,
a reference to the contract instance can obtained be as follows:
_10let account = getAccount(0x1)_10let contract: &TestInterface = account.contracts.borrow<&TestInterface>(name: "Test")
This is similar to the import statement
_10import Test from 0x1
Deploying a new contract
The contracts.add
function deploys a new contract to an account:
_10access(Contracts | AddContract)_10fun add(_10 name: String,_10 code: [UInt8],_10 ... contractInitializerArguments_10): DeployedContract
Calling the add
function requires access to an account via a reference which is authorized
with the coarse-grained Contracts
entitlement (auth(Contracts) &Account
),
or the fine-grained AddContract
entitlement (auth(AddContract) &Account
).
The code
parameter is the UTF-8 encoded representation of the source code.
The code must contain exactly one contract or contract interface,
which must have the same name as the name
parameter.
The add
function passes all extra arguments of the call (contractInitializerArguments
)
to the initializer of the contract.
If a contract with the given name already exists in the account, if the given code does not declare exactly one contract or contract interface, or if the given name does not match the name of the contract declaration in the code, then the function aborts the program.
When the deployment succeeded, the function returns the deployed contract.
For example, assuming the following contract code should be deployed:
_10access(all)_10contract Test {_10 access(all)_10 let message: String_10_10 init(message: String) {_10 self.message = message_10 }_10}
The contract can be deployed as follows:
_10transaction(code: String) {_10 prepare(signer: auth(AddContract) &Account) {_10 signer.contracts.add(_10 name: "Test",_10 code: code.utf8,_10 message: "I'm a new contract in an existing account"_10 )_10 }_10}
Updating a deployed contract
The contracts.update
function updates the code of an existing contract:
_10access(Contracts | UpdateContract)_10fun update(name: String, code: [UInt8]): DeployedContract
Calling the update
function requires access to an account via a reference which is authorized
with the coarse-grained Contracts
entitlement (auth(Contracts) &Account
),
or the fine-grained UpdateContract
entitlement (auth(UpdateContract) &Account
).
The code
parameter is the UTF-8 encoded representation of the source code.
The code must contain exactly one contract or contract interface,
which must have the same name as the name
parameter.
If no contract with the given name exists in the account, if the given code does not declare exactly one contract or contract interface, or if the given name does not match the name of the contract declaration in the code, then the function aborts the program.
When the update succeeded, the function returns the deployed contract.
The update
function does not run the initializer of the contract again.
Updating a contract does not change the contract instance and its existing stored data. A contract update only changes the code a contract.
Is only possible to update contracts in ways that keep data consistency. Certain restrictions apply.
For example, assuming that a contract named Test
is already deployed to the account,
and it should be updated with the following contract code:
_10access(all) contract Test {_10 access(all) let message: String_10_10 init(message: String) {_10 self.message = message_10 }_10}
The contract can be updated as follows:
_10transaction(code: String) {_10 prepare(signer: auth(UpdateContract) &Account) {_10 signer.contracts.update(_10 name: "Test",_10 code: code_10 )_10 }_10}
Removing a deployed contract
The contracts.remove
function removes a deployed contract from an account:
_10access(Contracts | RemoveContract)_10fun remove(name: String): DeployedContract?
Calling the remove
function requires access to an account via a reference which is authorized
with the coarse-grained Contracts
entitlement (auth(Contracts) &Account
),
or the fine-grained RemoveContract
entitlement (auth(RemoveContract) &Account
).
The function removes the contract from the account which has the given name and returns it.
If no contract with the given name exists in the account, the function returns nil
.
For example, assuming that a contract named Test
is deployed to an account,
the contract can be removed as follows:
_10transaction(code: String) {_10 prepare(signer: auth(RemoveContract) &Account) {_10 signer.contracts.remove(name: "Test",)_10 }_10}