Contract update with multiple _deploy() functions #1523

Open
opened 2025-12-28 17:16:44 +00:00 by sami · 1 comment
Owner

Originally created by @fyfyrchik on GitHub (May 19, 2025).

Context

As per https://github.com/nspcc-dev/neo-go/blob/master/docs/compiler.md#vm-api-interop-layer (bold is mine):

_deploy() function has a special meaning and is executed when contract is deployed. It should return no value and accept two arguments: the first one is data containing all values deploy is aware of and able to make use of; the second one is a bool argument which will be true on contract update. _deploy() functions are called for every imported package in the same order as init()

The logic is there from the time _deploy was implemented, and it was expicitly discussed: https://github.com/nspcc-dev/neo-go/pull/1452#discussion_r498858279

Since then, it was long forgotten (by me, the implementor) and has bitten my colleagues recently in a long debugging session.

Suggestion

I'd like to revisit that decision now:

  1. Current implementation makes it hard (or impossible) to import one contract from the other. This is worth supporting, though: reusing constants and struct types is convenient.
  2. Having written many contracts, I believe the analogy to init() in the linked discussion is flawed: _deploy makes sense for a contract, not for a package:
  • the same data argument is passed to all functions (no such problem for init)
  • _deploy is useful because it uses storage and the storage schema is different for different contract, so usage of the same _deploy() from multiple packages is limited

Any examples of contracts where current behaviour is needed are welcome.
This change is not backwards compatible, but the compiler could emit warnings/error, so that no unexpected things happen.

Originally created by @fyfyrchik on GitHub (May 19, 2025). ## Context As per https://github.com/nspcc-dev/neo-go/blob/master/docs/compiler.md#vm-api-interop-layer (bold is mine): >`_deploy()` function has a special meaning and is executed when contract is deployed. It should return no value and accept two arguments: the first one is data containing all values deploy is aware of and able to make use of; the second one is a bool argument which will be true on contract update. `_deploy()` functions are called **for every imported package** in the same order as `init()` The logic is there from the time `_deploy` was implemented, and it was expicitly discussed: https://github.com/nspcc-dev/neo-go/pull/1452#discussion_r498858279 Since then, it was long forgotten (by me, the implementor) and has bitten my colleagues recently in a long debugging session. ## Suggestion I'd like to revisit that decision now: 1. Current implementation makes it _hard_ (or impossible) to import one contract from the other. This is worth supporting, though: reusing constants and struct types is convenient. 2. Having written many contracts, I believe the analogy to `init()` in the linked discussion is flawed: `_deploy` makes sense for a contract, not for a package: - the same `data` argument is passed to all functions (no such problem for `init`) - `_deploy` is useful because it uses storage and the storage schema is different for different contract, so usage of the same `_deploy()` from multiple packages is limited Any examples of contracts where current behaviour is needed are welcome. This change is not backwards compatible, but the compiler could emit warnings/error, so that no unexpected things happen.
Author
Owner

@roman-khimov commented on GitHub (May 19, 2025):

import one contract from the other.

This is very likely to break in other places as well (init()), so I'd say it's not supported.

reusing constants and struct types is convenient.

That's easy to solve using regular Go packages, make a shared package with whatever you need to share, import it.

_deploy makes sense for a contract, not for a package

In some ways I can agree with that since it's an exported method effectively. Does it worth the change? Not sure. There are contracts we're not aware of that are written in Go and they can use and misuse it already.

@roman-khimov commented on GitHub (May 19, 2025): > import one contract from the other. This is very likely to break in other places as well (`init()`), so I'd say it's not supported. > reusing constants and struct types is convenient. That's easy to solve using regular Go packages, make a shared package with whatever you need to share, import it. > _deploy makes sense for a contract, not for a package In some ways I can agree with that since it's an exported method effectively. Does it worth the change? Not sure. There are contracts we're not aware of that are written in Go and they can use and misuse it already.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nspcc-dev/neo-go#1523
No description provided.