Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update code snippets and account creation inconsistency #2308

Merged
merged 3 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/1.concepts/protocol/account-id.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The simplest way to obtain a public / private key that represents an account is
```bash
near account create-account fund-later use-auto-generation save-to-folder ~/.near-credentials/implicit

# The file "~/.near-credentials/testnet/8bca86065be487de45e795b2c3154fe834d53ffa07e0a44f29e76a2a5f075df8.json" was saved successfully
# The file "~/.near-credentials/implicit/8bca86065be487de45e795b2c3154fe834d53ffa07e0a44f29e76a2a5f075df8.json" was saved successfully

# Here is your console command if you need to script it or re-run:
# near account create-account fund-later use-auto-generation save-to-folder ~/.near-credentials/implicit
Expand Down
101 changes: 66 additions & 35 deletions docs/2.build/2.smart-contracts/release/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@
id: upgrade
title: Updating Contracts
---
import {CodeTabs, Language, Github} from "@site/src/components/codetabs";
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

NEAR accounts separate their logic (contract's code) from their state (storage), allowing the code to be changed.
import {CodeTabs, Language, Github} from "@site/src/components/codetabs"; import
Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';

NEAR accounts separate their logic (contract's code) from their state (storage),
allowing the code to be changed.

Contract's can be updated in two ways:

1. **Through tools** such as [NEAR CLI](../../../4.tools/cli.md) or [near-api-js](../../../4.tools/near-api-js/quick-reference.md) (if you hold the account's [full access key](../../../1.concepts/protocol/access-keys.md)).
2. **Programmatically**, by implementing a method that [takes the new code and deploys it](#programmatic-update).
1. **Through tools** such as [NEAR CLI](../../../4.tools/cli.md) or
[near-api-js](../../../4.tools/near-api-js/quick-reference.md) (if you hold
the account's
[full access key](../../../1.concepts/protocol/access-keys.md)).
2. **Programmatically**, by implementing a method that
[takes the new code and deploys it](#programmatic-update).

---

## Updating Through Tools

Simply re-deploy another contract using your preferred tool, for example, using [NEAR CLI](../../../4.tools/cli.md):
Simply re-deploy another contract using your preferred tool, for example, using
[NEAR CLI](../../../4.tools/cli.md):

<Tabs groupId="cli-tabs">
<TabItem value="short" label="Short">
Expand Down Expand Up @@ -49,7 +55,9 @@ near contract deploy <accountId> use-file <route_to_wasm> without-init-call netw
---

## Programmatic Update

A smart contract can also update itself by implementing a method that:

1. Takes the new wasm contract as input
2. Creates a Promise to deploy it on itself

Expand Down Expand Up @@ -94,7 +102,12 @@ near contract call-function as-transaction <contract-account> update_contract fi
const code = fs.readFileSync("./path/to/wasm.wasm");

// Call the update_contract method
await wallet.callMethod({contractId: guestBook, method: "update_contract", args: code, gas: "300000000000000"});
await wallet.callMethod({
contractId: guestBook,
method: "update_contract",
args: code,
gas: "300000000000000",
});
```

</TabItem>
Expand All @@ -103,21 +116,25 @@ await wallet.callMethod({contractId: guestBook, method: "update_contract", args:

:::tip DAO Factories

This is how DAO factories [update their contracts](https://github.com/near-daos/sputnik-dao-contract/blob/main/sputnikdao-factory2/src/factory_manager.rs#L60)
This is how DAO factories
[update their contracts](https://github.com/near-daos/sputnik-dao-contract/blob/main/sputnikdao-factory2/src/factory_manager.rs#L60)

:::

---

## Migrating the State

Since the account's logic (smart contract) is separated from the account's state (storage),
**the account's state persists** when re-deploying a contract.
Since the account's logic (smart contract) is separated from the account's state
(storage), **the account's state persists** when re-deploying a contract.

Because of this, **adding methods** or **modifying existing ones** will yield **no problems**.
Because of this, **adding methods** or **modifying existing ones** will yield
**no problems**.

However, deploying a contract that **modifies or removes structures** stored in
the state will raise an error: `Cannot deserialize the contract state`, in which
case you can choose to:

However, deploying a contract that **modifies or removes structures** stored in the state will raise an
error: `Cannot deserialize the contract state`, in which case you can choose to:
1. Use a different account
2. Rollback to the previous contract code
3. Add a method to migrate the contract's state
Expand All @@ -126,30 +143,34 @@ error: `Cannot deserialize the contract state`, in which case you can choose to:

### The Migration Method

If you have no option but to migrate the state, then you need to implement a method that:
If you have no option but to migrate the state, then you need to implement a
method that:

1. Reads the current state of the contract
2. Applies different functions to transform it into the new state
3. Returns the new state

:::tip DAO Update

This is how DAOs [update themselves](https://github.com/near-daos/sputnik-dao-contract/blob/main/sputnikdao2/src/upgrade.rs#L59)
This is how DAOs
[update themselves](https://github.com/near-daos/sputnik-dao-contract/blob/main/sputnikdao2/src/upgrade.rs#L59)

:::

<hr className="subsection" />

### Example: Guest Book Migration

Imagine you have a Guest Book where you store messages, and the users can pay for such messages
to be "premium". You keep track of the messages and payments using the following state:
Imagine you have a Guest Book where you store messages, and the users can pay
for such messages to be "premium". You keep track of the messages and payments
using the following state:

<CodeTabs>
<Language value="js" language="js">

<Github fname="index.js"
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates-base.js"
start="16" end="37" />
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates/basic-updates-base.js"
start="12" end="33" />

</Language>

Expand All @@ -165,15 +186,15 @@ to be "premium". You keep track of the messages and payments using the following

#### Update Contract

At some point you realize that you could keep track of the `payments` inside of the `PostedMessage` itself,
so you change the contract to:
At some point you realize that you could keep track of the `payments` inside of
the `PostedMessage` itself, so you change the contract to:

<CodeTabs>
<Language value="js" language="js">

<Github fname="index.js"
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates-update.js"
start="23" end="45" />
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates/basic-updates-update.js"
start="21" end="43" />

</Language>

Expand All @@ -189,22 +210,26 @@ so you change the contract to:

#### Incompatible States

If you deploy the update into an initialized account the contract will fail to deserialize the account's state,
because:
1. There is an extra `payments` vector saved in the state (from the previous contract)
2. The stored `PostedMessages` are missing the `payment` field (as in the previous contract)
If you deploy the update into an initialized account the contract will fail to
deserialize the account's state, because:

1. There is an extra `payments` vector saved in the state (from the previous
contract)
2. The stored `PostedMessages` are missing the `payment` field (as in the
previous contract)

#### Migrating the State

To fix the problem, you need to implement a method that goes through the old state, removes the `payments` vector and
adds the information to the `PostedMessages`:
To fix the problem, you need to implement a method that goes through the old
state, removes the `payments` vector and adds the information to the
`PostedMessages`:

<CodeTabs>
<Language value="js" language="js">

<Github fname="index.js"
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates-update.js"
start="7" end="70" />
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates/basic-updates-update.js"
start="5" end="68" />

</Language>

Expand All @@ -218,11 +243,17 @@ adds the information to the `PostedMessages`:

</CodeTabs>

Notice that `migrate` is actually an [initialization method](/build/smart-contracts/anatomy/storage#initializing-the-state) that **ignores** the existing state (`[#init(ignore_state)]`), thus being able to execute and rewrite the state.
Notice that `migrate` is actually an
[initialization method](/build/smart-contracts/anatomy/storage#initializing-the-state)
that **ignores** the existing state (`[#init(ignore_state)]`), thus being able
to execute and rewrite the state.

:::tip

You can follow a migration step by step in the [official migration example](https://github.com/near-examples/update-migrate-rust/tree/main/basic-updates/base)
Javascript migration example testfile can be found on here: [test-basic-updates.ava.js](https://github.com/near/near-sdk-js/blob/develop/examples/__tests__/test-basic-updates.ava.js), run by this command: `pnpm run test:basic-update` in examples directory.
You can follow a migration step by step in the
[official migration example](https://github.com/near-examples/update-migrate-rust/tree/main/basic-updates/base)
Javascript migration example testfile can be found on here:
[test-basic-updates.ava.js](https://github.com/near/near-sdk-js/blob/develop/examples/__tests__/test-basic-updates.ava.js),
run by this command: `pnpm run test:basic-update` in examples directory.

:::
Loading