Skip to content

Instantly share code, notes, and snippets.

@Martoon-00
Last active May 13, 2020 17:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Martoon-00/dfe4e6392c8a656e9a10b87d71bd9387 to your computer and use it in GitHub Desktop.
Save Martoon-00/dfe4e6392c8a656e9a10b87d71bd9387 to your computer and use it in GitHub Desktop.
UStore documented

Upgradeable counter (SDU)

Sample of storage-driven upgradeable contract.

Version: 1

Contract upgradeability

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.

Storage


Storage

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)))

Permanent entrypoints


getCounter

Argument:

  • In Haskell: Void () Integer
  • In Michelson: (pair unit (lambda int int))
How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Call contract's getCounter entrypoint passing the constructed argument.

Possible errors:

  • VoidResult — Call to entrypoint has succeeded, reporting returned value as error.

getNothing

Argument:

  • In Haskell: Empty
  • In Michelson: unit
How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Call contract's getNothing entrypoint passing the constructed argument.

Possible errors:

Upgradeable entrypoints


epInc

Argument:

  • In Haskell: ()
  • In Michelson: unit
How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Wrap into UParam as epInc entrypoint.
    • In Haskell: mkUParam #epInc (·)
    • In Michelson: Pair "epInc" (pack (·))
  3. Call contract's run entrypoint passing the constructed argument.

Possible errors:


epAdd

Argument:

  • In Haskell: Natural
  • In Michelson: nat
How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Wrap into UParam as epAdd entrypoint.
    • In Haskell: mkUParam #epAdd (·)
    • In Michelson: Pair "epAdd" (pack (·))
  3. Call contract's run entrypoint passing the constructed argument.

Possible errors:


epGetCounterValue

Argument:

  • In Haskell: Void () Natural
  • In Michelson: (pair unit (lambda nat nat))
How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Wrap into UParam as epGetCounterValue entrypoint.
    • In Haskell: mkUParam #epGetCounterValue (·)
    • In Michelson: Pair "epGetCounterValue" (pack (·))
  3. Call contract's run entrypoint passing the constructed argument.

Possible errors:

Top-level entrypoints of upgradeable contract

These are top-level service entry points of the contract. For entrypoints containing the logic of the contract see other entrypoints sections.


run

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
  1. Construct an argument for the entrypoint.
  2. Call contract's run entrypoint passing the constructed argument.

Possible errors:


runPerm

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
  1. Construct an argument for the entrypoint.
  2. Call contract's runPerm entrypoint passing the constructed argument.

Possible errors:


upgrade

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
  1. Construct an argument for the entrypoint.
  2. Call contract's upgrade entrypoint passing the constructed argument.

Possible errors:


getVersion

This entry point is used to get contract version.

Argument:

  • In Haskell: View () Version
  • In Michelson: (pair unit (contract nat))
How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Call contract's getVersion entrypoint passing the constructed argument.


setAdministrator

This entry point is used to set the administrator address.

Argument:

How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Call contract's setAdministrator entrypoint passing the constructed argument.

Possible errors:


epwBeginUpgrade

This entry point is used to start an entrypoint wise upgrade of the contract.

Argument:

  • In Haskell: (current : Version, new : Version)
  • In Michelson: (pair (nat :current) (nat :new))
How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Call contract's epwBeginUpgrade entrypoint passing the constructed argument.

Possible errors:


epwApplyMigration

This entry point is used to apply a storage migration script as part of an upgrade.

Argument:

How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Call contract's epwApplyMigration entrypoint passing the constructed argument.

Possible errors:


epwSetCode

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
  1. Construct an argument for the entrypoint.
  2. Call contract's epwSetCode entrypoint passing the constructed argument.

Possible errors:


epwSetPermCode

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
  1. Construct an argument for the entrypoint.
  2. Call contract's epwSetPermCode entrypoint passing the constructed argument.

Possible errors:


epwFinishUpgrade

This entry point is used to mark that an upgrade has been finsihed.

Argument: none (pass unit)

How to call this entrypoint
  1. Construct an argument for the entrypoint.
  2. Call contract's epwFinishUpgrade entrypoint passing the constructed argument.

Possible errors:

Definitions

Types


()

Unit primitive.

Structure: ()

Final Michelson representation: unit


(a, b)

Pair primitive.

Final Michelson representation (example): (Integer,Natural) = pair int nat


(a, b, c, d, e)

Tuple of size 5.

Final Michelson representation (example): ((),(),(),(),()) = pair (pair unit unit) (pair unit (pair unit unit))


Address (no entrypoint)

This is similar to Michelson Address, but does not retain entrypoint name if it refers to a contract.

Final Michelson representation: address


BigMap

BigMap primitive.

Final Michelson representation (example): BigMap Integer Natural = big_map int nat


Bool

Bool primitive.

Final Michelson representation: bool


ByteString

Bytes primitive.

Final Michelson representation: bytes


Code (extended lambda)

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 pairand 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

Contract primitive with given type of parameter.

Final Michelson representation (example): ContractRef Integer = contract int


Empty

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


Integer

Signed number.

Final Michelson representation: int


List

List primitive.

Final Michelson representation (example): [Integer] = list int


Maybe

Option primitive.

Final Michelson representation (example): Maybe Integer = option int


MigrationScript

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)


Named entry

Some entries have names for clarity.

In resulting Michelson names may be mapped to annotations.

Final Michelson representation (example): number: Integer = int


Natural

Unsigned number.

Final Michelson representation: nat


Operation

Operation primitive.

Final Michelson representation: operation


Permanent

Parameter for permanent entrypoints.

Structure: one of

Final Michelson representation: or (pair unit (lambda int int)) unit


PermanentImpl

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))


SduEntryPoint

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

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))


Text

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


UContractRouter

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))


Upgradable parameter

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


Upgradeable storage

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


Version

Contract version.

Structure: version :Natural

Final Michelson representation: nat


View

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

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)

Errors

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 supply 5.

    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.


EmptySupplied

Class: Bad argument

Fires if: Value of type Empty has been supplied.

Representation: ("EmptySupplied", ()).


InternalError

Class: Internal

Fires if: Some internal error occured.

Representation: Textual error message, see Text.


SenderIsNotAdmin

Class: Action exception

Fires if: Entrypoint executed not by its administrator.

Representation: ("SenderIsNotAdmin", ()).


UparamArgumentUnpackFailed

Class: Bad argument

Fires if: Argument of wrong type is provided to the entrypoint.

Representation: ("UparamArgumentUnpackFailed", ()).


UpgContractIsNotPaused

Class: Action exception

Fires if: The contract is not in paused state (for migrations).

Representation: ("UpgContractIsNotPaused", ()).


UpgContractIsPaused

Class: Action exception

Fires if: The contract is in paused state (for migrations).

Representation: ("UpgContractIsPaused", ()).


UpgVersionMismatch

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).


VoidResult

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>)

Used upgradeable storage formats

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.


Some

This is a dummy template, usually designates that any format can be used here.

Contents: unspecified


V1

Template for version 1 of the contract.

Contents:

Encoding
  • key = pack ("counterValue")

  • value = pack (<field value>)


Encoding
  • key = pack ({} :: lambda, "epInc")

  • value = pack (<field value>)


Encoding
  • key = pack ({} :: lambda, "epAdd")

  • value = pack (<field value>)


Encoding
  • key = pack ({} :: lambda, "epGetCounterValue")

  • value = pack (<field value>)


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment