Sample of storage-driven upgradeable contract.
Version: 1
This contract uses upgradeability approach described here. This mechanism provides adminstrator-forced address-preserving upgradeability approach. For more information check out the doc referenced earlier.
Type which defines storage of the upgradeable contract. It contains UStore with data related to actual contract logic and fields which relate to upgradeability logic.
Structure: (dataMap :UStore
V1
, fields :StorageFields
)
Final Michelson representation: pair (big_map bytes bytes) (pair (pair (lambda (pair (pair string bytes) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes))) (lambda (pair (or (pair unit (lambda int int)) unit) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes)))) (pair address (pair nat bool)))
Argument:
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
getCounter
entrypoint passing the constructed argument.
Possible errors:
VoidResult
— Call to entrypoint has succeeded, reporting returned value as error.
Argument:
- In Haskell:
Empty
- In Michelson:
unit
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
getNothing
entrypoint passing the constructed argument.
Possible errors:
EmptySupplied
— Value of typeEmpty
has been supplied.
Argument:
- In Haskell:
()
- In Michelson:
unit
How to call this entrypoint
- Construct an argument for the entrypoint.
- Wrap into UParam as
epInc
entrypoint.- In Haskell:
mkUParam #epInc (·)
- In Michelson:
Pair "epInc" (pack (·))
- In Haskell:
- Call contract's
run
entrypoint passing the constructed argument.
Possible errors:
UparamArgumentUnpackFailed
— Argument of wrong type is provided to the entrypoint.
Argument:
- In Haskell:
Natural
- In Michelson:
nat
How to call this entrypoint
- Construct an argument for the entrypoint.
- Wrap into UParam as
epAdd
entrypoint.- In Haskell:
mkUParam #epAdd (·)
- In Michelson:
Pair "epAdd" (pack (·))
- In Haskell:
- Call contract's
run
entrypoint passing the constructed argument.
Possible errors:
UparamArgumentUnpackFailed
— Argument of wrong type is provided to the entrypoint.
Argument:
How to call this entrypoint
- Construct an argument for the entrypoint.
- Wrap into UParam as
epGetCounterValue
entrypoint.- In Haskell:
mkUParam #epGetCounterValue (·)
- In Michelson:
Pair "epGetCounterValue" (pack (·))
- In Haskell:
- Call contract's
run
entrypoint passing the constructed argument.
Possible errors:
-
UparamArgumentUnpackFailed
— Argument of wrong type is provided to the entrypoint. -
VoidResult
— Call to entrypoint has succeeded, reporting returned value as error.
These are top-level service entry points of the contract. For entrypoints containing the logic of the contract see other entrypoints sections.
This entrypoint extracts contract code kept in storage under the corresponding name and executes it on an argument supplied via UParam
.
Argument:
- In Haskell:
UParam
- In Michelson:
(pair string bytes)
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
run
entrypoint passing the constructed argument.
Possible errors:
UpgContractIsPaused
— The contract is in paused state (for migrations).
Similar to Run
entrypoint, but calls permanent entrypoints - ones that will be present in all versions of the contract.
Argument:
- In Haskell:
Permanent
- In Michelson:
(or (pair unit (lambda int int)) unit)
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
runPerm
entrypoint passing the constructed argument.
Possible errors:
UpgContractIsPaused
— The contract is in paused state (for migrations).
This entry point is used to update the contract to a new version.
Consider using this entrypoint when your upgrade to the new version isn't very large,
otherwise, transaction with this entrypoint call won't fit instruction size limit.
If this is your case, consider using entrypoint-wise upgrade. This entrypoint
basically exchange code
field in the storage and upgrade dataMap
using
provided migration lambda.
Argument:
- In Haskell: (currentVersion :
Version
, newVersion :Version
, migrationScript :MigrationScript
V1
Some
, newCode :Maybe
UContractRouter
, newPermCode :Maybe
PermanentImpl
) - In Michelson:
(pair (pair (nat :currentVersion) (nat :newVersion)) (pair (lambda :migrationScript (big_map bytes bytes) (big_map bytes bytes)) (pair (option :newCode (lambda (pair (pair string bytes) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes)))) (option :newPermCode (lambda (pair (or (pair unit (lambda int int)) unit) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes)))))))
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
upgrade
entrypoint passing the constructed argument.
Possible errors:
-
SenderIsNotAdmin
— Entrypoint executed not by its administrator. -
UpgContractIsPaused
— The contract is in paused state (for migrations). -
UpgVersionMismatch
— Current contract version differs from the one passed in the upgrade.
This entry point is used to get contract version.
Argument:
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
getVersion
entrypoint passing the constructed argument.
This entry point is used to set the administrator address.
Argument:
- In Haskell:
Address (no entrypoint)
- In Michelson:
address
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
setAdministrator
entrypoint passing the constructed argument.
Possible errors:
SenderIsNotAdmin
— Entrypoint executed not by its administrator.
This entry point is used to start an entrypoint wise upgrade of the contract.
Argument:
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
epwBeginUpgrade
entrypoint passing the constructed argument.
Possible errors:
-
SenderIsNotAdmin
— Entrypoint executed not by its administrator. -
UpgContractIsPaused
— The contract is in paused state (for migrations). -
UpgVersionMismatch
— Current contract version differs from the one passed in the upgrade.
This entry point is used to apply a storage migration script as part of an upgrade.
Argument:
- In Haskell:
MigrationScript
V1
Some
- In Michelson:
(lambda (big_map bytes bytes) (big_map bytes bytes))
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
epwApplyMigration
entrypoint passing the constructed argument.
Possible errors:
-
SenderIsNotAdmin
— Entrypoint executed not by its administrator. -
UpgContractIsNotPaused
— The contract is not in paused state (for migrations).
This entry point is used to set the dispatching code that calls the packed entrypoints.
Argument:
- In Haskell:
UContractRouter
- In Michelson:
(lambda (pair (pair string bytes) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes)))
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
epwSetCode
entrypoint passing the constructed argument.
Possible errors:
-
SenderIsNotAdmin
— Entrypoint executed not by its administrator. -
UpgContractIsNotPaused
— The contract is not in paused state (for migrations).
Similar to EpwSetCode
, but refers to permanent entrypoints - ones that will be present in all versions of the contract.
Argument:
- In Haskell:
PermanentImpl
- In Michelson:
(lambda (pair (or (pair unit (lambda int int)) unit) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes)))
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
epwSetPermCode
entrypoint passing the constructed argument.
Possible errors:
-
SenderIsNotAdmin
— Entrypoint executed not by its administrator. -
UpgContractIsNotPaused
— The contract is not in paused state (for migrations).
This entry point is used to mark that an upgrade has been finsihed.
Argument: none (pass unit)
How to call this entrypoint
- Construct an argument for the entrypoint.
- Call contract's
epwFinishUpgrade
entrypoint passing the constructed argument.
Possible errors:
-
SenderIsNotAdmin
— Entrypoint executed not by its administrator. -
UpgContractIsNotPaused
— The contract is not in paused state (for migrations).
Unit primitive.
Structure: ()
Final Michelson representation: unit
Pair primitive.
Final Michelson representation (example): (Integer,Natural)
= pair int nat
Tuple of size 5.
Final Michelson representation (example): ((),(),(),(),())
= pair (pair unit unit) (pair unit (pair unit unit))
This is similar to Michelson Address, but does not retain entrypoint name if it refers to a contract.
Final Michelson representation: address
BigMap primitive.
Final Michelson representation (example): BigMap Integer Natural
= big_map int nat
Bool primitive.
Final Michelson representation: bool
Bytes primitive.
Final Michelson representation: bytes
Code i o
stands for a sequence of instructions which accepts stack of type i
and returns stack of type o
.
When both i
and o
are of length 1, this primitive corresponds to the Michelson lambda. In more complex cases code is surrounded with pair
and unpair
instructions until fits into mentioned restriction.
Final Michelson representation (example): Code [Integer, Natural, MText, ()] [ByteString]
= lambda (pair (pair (pair int nat) string) unit) bytes
Contract primitive with given type of parameter.
Final Michelson representation (example): ContractRef Integer
= contract int
Type which should never be constructed.
If appears as part of entrypoint argument, this means that the entrypoint should never be called.
Structure: ()
Final Michelson representation: unit
Signed number.
Final Michelson representation: int
List primitive.
Final Michelson representation (example): [Integer]
= list int
Option primitive.
Final Michelson representation (example): Maybe Integer
= option int
A code which updates storage in order to make it compliant with the new version of the contract. It is common to have a group of migration scripts because each of it is to be used in Tezos transaction and thus should fit into gas and operation size limits.
Structure: unMigrationScript :Code
[UStore
Some
] [UStore
Some
]
Final Michelson representation: lambda (big_map bytes bytes) (big_map bytes bytes)
Some entries have names for clarity.
In resulting Michelson names may be mapped to annotations.
Final Michelson representation (example): number: Integer
= int
Unsigned number.
Final Michelson representation: nat
Operation primitive.
Final Michelson representation: operation
Parameter for permanent entrypoints.
Structure: one of
Final Michelson representation: or (pair unit (lambda int int)) unit
Implementation of permanent entrypoints.
Structure: unPermanentImpl :Code
[Permanent
, UStore
V1
] [(List
Operation
, UStore
V1
)]
Final Michelson representation: lambda (pair (or (pair unit (lambda int int)) unit) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes))
Public upgradeable entrypoint of a contract.
Structure (example): SduEntryPoint () Integer
= Code
[(ByteString
, UStore
empty
)] [(List
Operation
, UStore
empty
)]
Final Michelson representation (example): SduEntryPoint () Integer
= lambda (pair bytes (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes))
StorageFields of upgradeable contract. This type keeps general information about upgradeable contract and the logic responsible for calling entrypoints implementations kept in UStore.
Structure: (code :UContractRouter
, permCode :PermanentImpl
, admin :Address (no entrypoint)
, currentVersion :Version
, paused :Bool
)
Final Michelson representation: pair (pair (lambda (pair (pair string bytes) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes))) (lambda (pair (or (pair unit (lambda int int)) unit) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes)))) (pair address (pair nat bool))
Michelson string.
This has to contain only ASCII characters with codes from [32; 126] range; additionally, newline feed character is allowed.
Final Michelson representation: string
Parameter dispatching logic, main purpose of this code is to pass control to an entrypoint carrying the main logic of the contract.
Structure: unUContractRouter :Code
[(UParam
, UStore
V1
)] [(List
Operation
, UStore
V1
)]
Final Michelson representation: lambda (pair (pair string bytes) (big_map bytes bytes)) (pair (list operation) (big_map bytes bytes))
This type encapsulates parameter for one of entry points. It keeps entry point name and corresponding argument serialized.
Structure: (Text
, ByteString
)
Final Michelson representation: pair string bytes
Storage with not hardcoded structure, which allows upgrading the contract in place. UStore is capable of storing simple fields and multiple submaps.
Structure: unUStore :BigMap
ByteString
ByteString
Final Michelson representation: big_map bytes bytes
Contract version.
Structure: version :Natural
Final Michelson representation: nat
View a r
accepts an argument of type a
and callback contract which accepts r
and returns result via calling that contract.
Read more in A1 conventions document.
Structure (example): View () Integer
= (()
, ContractRef
Integer
)
Final Michelson representation (example): View () Integer
= pair unit (contract int)
Void a r
accepts an argument of type a
and returns a value of type r
as contract error. To comply with general mechanism of contracts custom errors, void entrypoints execute FAILWITH
instruction on ("VoidResult", r)
value, where r
is the actual return value of the entrypoint.
Read more in A1 conventions document.
Structure (example): Void () Integer
= (()
, Code
[Integer
] [Integer
])
Final Michelson representation (example): Void () Integer
= pair unit (lambda int int)
Our contract implies the possibility of error scenarios, this section enlists
all values which the contract can produce via calling FAILWITH
instruction
on them. In case of error, no changes to contract state will be applied.
Each entrypoint also contains a list of errors which can be raised during its execution; only for no-throw entrypoints this list will be omitted. Errors in these lists are placed in the order in which the corresponding properties are checked unless the opposite is specified. I.e., if for a given entrypoint call two different errors may take place, the one which appears in the list first will be thrown.
Most of the errors are represented according to the same
(error tag, error argument)
pattern. See the list of errors below
for details.
We distinquish several error classes:
-
Action exception: given action cannot be performed with regard to the current contract state.
Examples: "insufficient balance", "wallet does not exist".
If you are implementing a middleware, such errors should be propagated to the client.
-
Bad argument: invalid argument supplied to the entrypoint.
Examples: entrypoint accepts a natural number from
0-3
range, and you supply5
.If you are implementing a middleware, you should care about not letting such errors happen.
-
Internal: contract-internal error.
In ideal case, such errors should not take place, but still, make sure that you are ready to handle them. They can signal either invalid contract deployment or a bug in contract implementation.
If an internal error is thrown, please report it to the author of this contract.
Class: Bad argument
Fires if: Value of type Empty
has been supplied.
Representation: ("EmptySupplied", ())
.
Class: Internal
Fires if: Some internal error occured.
Representation: Textual error message, see Text
.
Class: Action exception
Fires if: Entrypoint executed not by its administrator.
Representation: ("SenderIsNotAdmin", ())
.
Class: Bad argument
Fires if: Argument of wrong type is provided to the entrypoint.
Representation: ("UparamArgumentUnpackFailed", ())
.
Class: Action exception
Fires if: The contract is not in paused state (for migrations).
Representation: ("UpgContractIsNotPaused", ())
.
Class: Action exception
Fires if: The contract is in paused state (for migrations).
Representation: ("UpgContractIsPaused", ())
.
Class: Action exception
Fires if: Current contract version differs from the one passed in the upgrade.
Representation: ("UpgVersionMismatch", <error argument>)
.
Provided error argument will be of type (expectedCurrent : Version
, actualCurrent : Version
).
Class: -
Fires if: Call to entrypoint has succeeded, reporting returned value as error. As Tezos contracts normally do not produce any output (not counting storage update), this is the simplest way to return something to the caller in read-only entrypoints.
Representation: ("VoidResult", <return value>)
This section describes formats (aka templates) of upgradeable storages mentioned across the given document. Each format describes set of fields and virtual submaps which the storage must have.
This is a dummy template, usually designates that any format can be used here.
Contents: unspecified
Template for version 1 of the contract.
Contents:
- Field
counterValue
:Natural
Encoding
-
key = pack ("counterValue")
-
value = pack (<field value>)
- Field
epInc
:SduEntryPoint
()
Encoding
-
key = pack ({} :: lambda, "epInc")
-
value = pack (<field value>)
- Field
epAdd
:SduEntryPoint
Natural
Encoding
-
key = pack ({} :: lambda, "epAdd")
-
value = pack (<field value>)
- Field
epGetCounterValue
:SduEntryPoint
(Void
()
Natural
)
Encoding
-
key = pack ({} :: lambda, "epGetCounterValue")
-
value = pack (<field value>)