Skip to content

Commit

Permalink
fix: update code snippets and account creation inconsistency (#2308)
Browse files Browse the repository at this point in the history
* chore: update code snippets

* fix: inconsistency in implicit account creation
  • Loading branch information
flmel authored Nov 15, 2024
1 parent be0373b commit c358c7f
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 36 deletions.
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.

:::

0 comments on commit c358c7f

Please sign in to comment.