xJonathanLEI / starknet-rs

Complete Starknet library in Rust™
https://starknet.rs
Apache License 2.0
277 stars 96 forks source link

Revamp contract deployment #232

Closed xJonathanLEI closed 1 year ago

xJonathanLEI commented 1 year ago

Introduction

A lot has changed regarding StarkNet contract deployment since we first implemented ContractFactory for deployment contracts:

So, it's time for a revamp of the contract deployment process in starknet-rs to make it more streamlined and robust. Hopefully this document will serve to explain the design decision behind, and to help make things around StarkNet contract deployment clearer as they're kinda messy right now with everything (new & deprecated) floating around.

How contract deployment works

Note that at the moment, starknet-rs is not interested in supporting deprecated features (since they will be removed after regenesis anyways). Raw data types for sending Deploy and Declare v0 transaction types will be kept until they're removed from the network itself, but higher-level constructs won't make use of them. Here we pretend all deprecated features are gone already.

The contract deployment process consists of 2 steps:

Declaration

To deploy a contract, you first need to declare its class. The only way to do that is to send a Declare v1 transaction from an account contract. This is also the only step where you would need the full contract definition. You only need the class hash for the actual deployment.

Deployment

This is the interesting part: there are actually 3 ways of deploying contracts:

  1. Using the deploy syscall inside an Invoke v1 transaction. However, this is equivalent to CREATE in Ethereum, which is mostly none of an SDK's business.
  2. Using the Universal Deployer contract. This is technically the same thing as method 1, but it's "standardized" (well, by OpenZeppelin, so maybe @martriay can shed some light on the level of commitment on interface and behavior) so we can kind of rely on it and code it into starknet-rs. This needs to go through an account contract.
  3. Using the DeployAccount transaction type, where transaction fees are pre-funded to the target contract address. This will likely be the main way of account contract deployment going forward, so we definitely need to support it. This also needs to go through a (future) account contract.

One side note here is that DeployAccount transactions can actually be used to deploy any contract. See my little experiment here. However, my current best guess here is that it's not supposed to be used that way. Unless that turns out to be wrong, I don't think we should support it. (Users will still be able to use it that way with the raw data types if they really want to)

So it looks like every step to be supported in starknet-rs involves an account (with DeployAccount it's actually a "future" account), which is nice as it makes things consistent.

Designing a clean interface

This is just some very rough idea I have in mind. Issues or overlooked use cases might be found when actually implementing these.

Interfaces

Currently, contract declaration is implemented as part of the Account trait via the declare() method, which takes a ContractDefinition type. I'm happy with how it works right now. So I'm not gonna touch this part for now, though I definitely want to change the return type into something wait-able and yield something that can be used for deployment directly (instead of the barebone AddTransactionResult we have now). But that's a separate task for the future.

For deployment, I plan to replace the current ContractFactory type with 2 new types:

Both types should be able to get "prepared" into an intermediate type where users can adjust parameters like fees, like what you can do with AttachedAccountCall right now. Such an intermediate type should also provide an API for calculating the resulting contract address if deployed. This is very especially important for AccountFactory since users will need to fund the address first.

Design rationale

xJonathanLEI commented 1 year ago

Damn. I just wanted to jot some notes on the revamp plan but ended up writing an essay lol.

Spamming your inboxes since you guys contributed a lot to this library: @milancermak @fracek haha. Just FYI, can totally ignore, but feedbacks are welcome!

martriay commented 1 year ago

great initiative! not sure what kind of light can I shed but the address it's expected to be 0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf when deployed from 0 with salt 0.

the verified code can be found on the above link, with the same interface as in the UDC Proposal. further documentation on usage, caveats, and design rationale is on the way.

xJonathanLEI commented 1 year ago

@martriay Thanks! I was mainly wondering if the interface and address calculation behavior (mainly salt) will change cuz I will be hard-coding it inside the library, along with the address of latest UDC, so it might silently break users at runtime if the interface changes.

But hey, StarkNet itself still makes breaking changes all the time. Maybe at this point it doesn't matter lol. Or maybe a better approach is simply to not ship with an address.

martriay commented 1 year ago

the address is expected to stay, although any future changes to the code will result in a different one. interface changes are even less likely, but it will depend on the ecosystem. what we do in nile is to take an UDC address parameter with a default value for the standard one. this also allows users to use their own deployer contracts even if it's not the UDC

xJonathanLEI commented 1 year ago

Yeah I figure I wasn't thinking straight and thought it might break people if the default address changes lol. It's actually the opposite: it breaks if users supply their own address and the UDC interface changes. All good now. Thanks!

fracek commented 1 year ago

I agree with this, I was going to suggest a trait for account deployers but I see you already defined it.