Skip to content

Commit

Permalink
impl
Browse files Browse the repository at this point in the history
  • Loading branch information
pugachAG committed Feb 12, 2025
1 parent cdcbefb commit 39b0c80
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Changes to the protocol specification and standards are called NEAR Enhancement
| [0519](https://github.com/near/NEPs/blob/master/neps/nep-0519.md) | Yield Execution | @akhi3030 @saketh-are | Final |
| [0536](https://github.com/near/NEPs/blob/master/neps/nep-0536.md) | Reduce the number of gas refunds | @evgenykuzyakov @bowenwang1996 | Final |
| [0539](https://github.com/near/NEPs/blob/master/neps/nep-0539.md) | Cross-Shard Congestion Control | @wacban @jakmeier | Final |
| [0539](https://github.com/near/NEPs/blob/master/neps/nep-0591.md) | Global Contracts | @bowenwang1996 @pugachag | Final |

## Specification

Expand Down
182 changes: 182 additions & 0 deletions neps/nep-0591.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
---
NEP: 591
Title: Global Contracts
Authors: Bowen Wang <bowen@nearone.org>, Anton Puhach <anton@nearone.org>, Stefan Neamtu <stefan.neamtu@nearone.org>
Status: Draft
DiscussionsTo: https://github.com/nearprotocol/neps/pull/591
Type: Protocol
Replaces: 491
Version: 1.0.0
Created: 2025-02-11
LastUpdated: 2025-02-11
---

## Summary

This proposal introduces global contracts, a new mechanism that allows smart contracts to be deployed once and reused by any account without incurring high storage costs.

Currently, deploying the same contract multiple times on different accounts leads to significant storage fees.
Global contracts solve this by making contract code available globally, allowing multiple accounts to reference it instead of storing their own copies.

Rather than requiring full storage costs for each deployment, accounts can simply link to an existing global contract, reducing redundancy and improving scalability. This approach optimizes storage, lowers costs, and ensures efficient contract distribution across the network.

## Motivation

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.

Additionally global contracts cover the underlying use case for [NEP-491](https://github.com/near/NEPs/pull/491): https://github.com/near/nearcore/pull/12818.

## Specification

Global contract can be deployed in 2 ways: either by its hash or by owner account id.
Contracts deployed by hash are effectively immutable and cannot be updated.
When deployed by account id the owner can redeploy the contract updating it for all its users.

We introduce new receipt action for deploying global contracts:
```

Check failure on line 40 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should be surrounded by blank lines [Context: "```"]

neps/nep-0591.md:40 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"]

Check failure on line 40 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should have a language specified [Context: "```"]

neps/nep-0591.md:40 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
struct DeployGlobalContractAction {
code: Vec<u8>,
deploy_mode: GlobalContractDeployMode,
}
enum GlobalContractDeployMode {
/// Contract is deployed under its code hash.
/// Users will be able reference it by that hash.
/// This effectively makes the contract immutable.
CodeHash,
/// Contract is deployed under the owner account id.
/// Users will be able reference it by that account id.
/// This allows the owner to update the contract for all its users.
AccountId,
}
```

Also new action is added for using previously deployed global contract:
```

Check failure on line 59 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should be surrounded by blank lines [Context: "```"]

neps/nep-0591.md:59 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"]

Check failure on line 59 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should have a language specified [Context: "```"]

neps/nep-0591.md:59 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
struct UseGlobalContractAction {
contract_identifier: GlobalContractIdentifier,
}
enum GlobalContractIdentifier {
CodeHash(CryptoHash),
AccountId(AccountId),
}
```

## Reference Implementation

### Storage

In order to have global contracts available to users on all shards we store a copy in each shard's trie.
A new trie key is introduced for that:
```

Check failure on line 76 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should be surrounded by blank lines [Context: "```"]

neps/nep-0591.md:76 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"]

Check failure on line 76 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should have a language specified [Context: "```"]

neps/nep-0591.md:76 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
pub enum TrieKey {
...
GlobalContractCode {
identifier: GlobalContractCodeIdentifier,
},
}
pub enum GlobalContractCodeIdentifier {
CodeHash(CryptoHash),
AccountId(AccountId),
}
```

Check failure on line 88 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should be surrounded by blank lines [Context: "```"]

neps/nep-0591.md:88 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"]
The value is contract code bytes, similar to `TrieKey::ContractCode`.

### Distribution

Global contract has to be distributed to all shards after being deployed.
This is implemented with a dedicated receipt type:
```

Check failure on line 95 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should be surrounded by blank lines [Context: "```"]

neps/nep-0591.md:95 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"]

Check failure on line 95 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should have a language specified [Context: "```"]

neps/nep-0591.md:95 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
enum ReceiptEnum {
...
GlobalContractDistribution(GlobalContractData),
}
struct GlobalContractData {
code: Vec<u8>,
id: GlobalContractIdentifier,
}
```

`GlobalContractDistribution` receipt is generated as a result of processing `DeployGlobalContractAction`.
Receipt distribution logic is updated to route such receipts to all shards.
So effectively it is a part of `ShardProof` (incoming receipts) for each shard, but occurs only once in the outgoing receipts of that chunk.
Applying `GlobalContractDistribution` receipt updates the corresponding `TrieKey::GlobalContractCode` in the trie.

### Usage

We change `Account` struct to make it possible to reference global contracts.
`AccountV2` is introduced changing `code_hash: CryptoHash` field to more generic `contract: AccountContract`:
```

Check failure on line 116 in neps/nep-0591.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Fenced code blocks should be surrounded by blank lines [Context: "```"]

neps/nep-0591.md:116 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"]
enum AccountContract {
None,
Local(CryptoHash),
Global(CryptoHash),
GlobalByAccount(AccountId),
}
```
Applying `UseGlobalContractAction` updates user account `contract` field accordingly.

`FunctionCall` action processing is updated to respect global contracts. This includes updating [contract preparation pipeline](https://github.com/near/nearcore/blob/fb95d7b7740d1fda9245afa498ce4e9ac145c8af/runtime/runtime/src/pipelining.rs#L24) as well as [recording of the executed contract to be included in the state witness](https://github.com/near/nearcore/blob/fb95d7b7740d1fda9245afa498ce4e9ac145c8af/core/store/src/trie/update.rs#L338).

### Costs

For global contracts we burn tokens for storage instead of locking like what we do regular contracts today.
The cost per byte of global contract code `global_contract_storage_amount_per_byte` is set as 10x the storage staking cost `storage_amount_per_byte`.

Additionally we add action costs for the global contract related actions:
* `action_deploy_global_contract` is exactly the same as `action_deploy_contract`
* `action_deploy_global_contract_per_byte`:
* send costs are the same as `action_deploy_contract_per_byte`
* execution costs should cover distribution of the contract to all shards:
* this is pretty expensive for the network, so want want to change significant amount of gas for that
* we still want to be able to fit max allowed contracts size into single chunk space: `max_gas_burnt = 300_000_000_000_000`, `max_contract_size = 4_194_304`, so it should be at most `max_gas_burnt / max_contract_size = 71_525_573`
* we need to allow for some margin for other costs, so we can round it down to `70_000_000`

TODO(stedfn): cover costs for using global contracts

## Security Implications

One potential issue is increasing infrastructure cost for global contracts with growing number of shards.
A global contract is effectively replicated on every shard, so with increase in number of shards each global contract uses more storage.
This can be potentially addressed in the future by making deployment costs parametrized with the number of shards in the current epoch, but it still wouldn't address the issue for the already deployed contracts.

## Alternatives

In [the original proposal](https://github.com/near/NEPs/issues/556) we considered storing global contracts in a separate global trie (managed at the block level) and introducing a dedicated distribution mechanism.
We decided not to proceed with this approach as it requires significantly higher effort to implement and also introduces new critical dependencies for the protocol.

## Changelog

[The changelog section provides historical context for how the NEP developed over time. Initial NEP submission should start with version 1.0.0, and all subsequent NEP extensions must follow [Semantic Versioning](https://semver.org/). Every version should have the benefits and concerns raised during the review. The author does not need to fill out this section for the initial draft. Instead, the assigned reviewers (Subject Matter Experts) should create the first version during the first technical review. After the final public call, the author should then finalize the last version of the decision context.]

### 1.0.0 - Initial Version

> Placeholder for the context about when and who approved this NEP version.
#### Benefits

> List of benefits filled by the Subject Matter Experts while reviewing this version:
* Benefit 1
* Benefit 2

#### Concerns

> Template for Subject Matter Experts review for this version:
> Status: New | Ongoing | Resolved
| # | Concern | Resolution | Status |
| --: | :------ | :--------- | -----: |
| 1 | | | |
| 2 | | | |

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

0 comments on commit 39b0c80

Please sign in to comment.