Skip to content

Shared contract code #556

Open
Open
@bowenwang1996

Description

A common use case on NEAR is to deploy the same smart contract many times on many different accounts. For example, a multisig contract is a frequently deployed contract. However, today each time such a contract is deployed, a user has to pay for its storage and the cost is quite high. For a 300kb contract the cost is 3N. With the advent of chain signatures, the smart contract wallet use case will become more ubiquitous. As a result, it is very desirable to be able to reuse already deployed contract without having to pay for the storage cost again. At the same time, the new stateless validation architecture also needs to better way to distribute contract code so that it doesn't bloat state witness.

The proposal is as follows. A new deploy contract action will be introduced (tentatively called DeployPermanentContractAction). This action, when processed, has a few differences from the DeployContractAction:

  • It generates a message that is sent to all validators, if the contract compilation is successful. We will expand more on this mechanism below.
  • It burns tokens for storage instead of lock tokens for storage, like what we do today. The exact amount to burn is TBD, but 1N for every 10kb of contract code seems reasonable, which means that a 300kb contract will burn 30N. It is intentionally expensive to prevent abusing this action to store data onchain.
  • It doesn't deploy the contract to any specific account. This means that no account's existing storage staking mechanism will be affected.

After runtime process this DeployPermanentContractAction, it generates a list of permanent_contracts. The chunk producer for this chunk is then repsonsible for broadcasting these contracts to all validators. Chunk headers include a vector of hashes of new permanently deployed contracts. In the block header, a permanent_contracts_root is maintained as the merkle root of hashes of all permanently deployed contracts. When a new validator node joins the network, it can synchronize all permanently deployed contracts from other nodes using the permanent_contracts_root of a specific block.

We will also introduce a new action DeployExistingContractAction, which, instead of taking the whole contract as an argument, simply takes a contract hash. This action attempts to deploy a permanent contract onto an account and will fail if no such permanent contract exists. The gas cost of this action should be the same as the gas cost of updating an account, since no deployment actually happens when this action is processed.

Because permanently deployed contract code is now stored on every validator node, it does not need to be included as part of state witness. Permanently deployed contract code is not stored as part of the state. They, alongside with their compilation results, are stored separately in a key value based storage.

A summary of changes/addition of new structs can be found below:

/// Permanently deploy a contract. In addition to gas costs, it also burns NEAR for storage
DeployPermanentContractAction {
  code: Vec<u8>
}
/// Deploys a permanent contract to an account. Fails if the specified hash does not correspond to any contract code
DeployExistingContractAction {
  code_hash: CryptoHash
}
ShardChunkHeader {
  ...
  permanent_contract_hashes: Vec<CryptoHash>
}
BlockHeader {
  ...
  permanent_contracts_root: CryptoHash,
}

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • Status

      NEW❗

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions