Updating Contracts
NEAR 계정은 로직(컨트랙트의 코드)을 상태(스토리지)와 분리하여, 코드를 변경할 수 있습니다.
컨트랙트는 두 가지 방법으로 업데이트할 수 있습니다.
- Through tools such as NEAR CLI or near-api-js (if you hold the account's full access key).
- Programmatically, by implementing a method that takes the new code and deploys it.
Updating Through Tools
NEAR CLI 등 선호하는 도구를 사용하여 다른 컨트랙트를 재배포하기만 하면 됩니다.
- near-cli
- near-cli-rs
# (optional) If you don't have an account, create one
near create-account <account-id> --useFaucet
# Deploy the contract
near deploy <account-id> <wasm-file>
# (optional) If you don't have an account, create one
near account create-account sponsor-by-faucet-service somrnd.testnet autogenerate-new-keypair save-to-keychain network-config testnet create
# Deploy the contract
near contract deploy <accountId> use-file <route_to_wasm> without-init-call network-config testnet sign-with-keychain send
프로그래밍을 통한 업데이트
스마트 컨트랙트를 다음과 같은 방법을 구현하여 자체적으로 업데이트할 수도 있습니다.
- Takes the new wasm contract as input
- Promise를 생성하여 자체적으로 배포합니다.
- 🦀 Rust
Loading...
How to Invoke Such Method?
- near-cli
- near-cli-rs
- 🌐 JavaScript
# Load the contract's raw bytes
CONTRACT_BYTES=`cat ./path/to/wasm.wasm | base64`
# Call the update_contract method
near call <contract-account> update_contract "$CONTRACT_BYTES" --base64 --accountId <manager-account> --gas 300000000000000
# Call the update_contract method
near contract call-function as-transaction <contract-account> update_contract file-args </path/to/wasm.wasm> prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as <manager-account> network-config testnet sign-with-keychain send
// Load the contract's raw bytes
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"});
This is how DAO factories update their contracts
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.
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:
- 다른 계정 사용
- Rollback to the previous contract code
- 컨트랙트 상태를 마이그레이션하는 메서드 추가
마이그레이션 메서드
상태를 마이그레이션하는 것 외에 다른 옵션이 없는 경우 다음과 같은 메서드를 구현해야 합니다.
- Reads the current state of the contract
- 새로운 상태로 변환하기 위해 다른 함수를 적용합니다.
- 새로운 상태를 반환합니다.
이것이 DAO가 스스로를 업데이트하는 방법입니다.
예제: 방명록 마이그레이션
메시지를 저장하는 방명록이 있고, 사용자가 이러한 메시지에 대해 "프리미엄"으로 지불할 수 있다고 상상해 보세요. 다음과 같은 상태를 사용하여 메시지 및 결제를 추적할 수 있습니다.
- 🌐 Javascript
- 🦀 Rust
Loading...
Loading...
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:
- 🌐 Javascript
- 🦀 Rust
Loading...
Loading...
Incompatible States
If you deploy the update into an initialized account the contract will fail to deserialize the account's state, because:
- There is an extra
payments
vector saved in the state (from the previous contract) - The stored
PostedMessages
are missing thepayment
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
:
- 🌐 Javascript
- 🦀 Rust
Loading...
Loading...
Notice that migrate
is actually an initialization method that ignores the existing state ([#init(ignore_state)]
), thus being able to execute and rewrite the state.
You can follow a migration step by step in the official migration example
Javascript migration example testfile can be found on here: test-basic-updates.ava.js, run by this command: pnpm run test:basic-update
in examples directory.