Upgrading the Module

How to upgrade your module?

Aptos에서는 이미 배포가 되어있는 module이라도 추가적인 코드를 작성하여 업그레이드 할 수 있습니다. 이를 Module Upgrade 라고 하며 이번 튜토리얼에서는 기존에 작성된 Module을 이용해 업데이트 하는 방법을 알아보겠습니다.

🚧

본 튜토리얼을 진행하기 위해서는 사용자가 배포한 Fungible Asset Module과 배포 환경이 필요합니다!

배포한 모듈이 없는 경우 아래 링크를 클릭하여 Nodit Aptos Tutorial을 따라 Message Module을 배포해 보세요!


📘

이 튜토리얼을 통해 아래 내용을 배울 수 있습니다!

  • Module의 함수를 호출하는 방법을 알 수 있습니다.
  • 새로운 함수를 작성하여 Module에서 이용하는 로직을 변경하는 방법을 알 수 있습니다.
  • 전체 튜토리얼 코드는 아래 링크를 통해 확인할 수 있습니다.
  • Nodit Aptos Upgrading the Module 튜토리얼 코드 확인하기

Upgrade your Module

Nodit 튜토리얼을 통해 배포한 Message Module의 코드는 다음과 같습니다.

module ownerAddress::message {
  use std::signer;
  use std::string::{Self,utf8, String};
  use std::error;

  struct Message has key {
    message_counter : u64,
    message : String,
  }

  const ENO_MESSAGE: u64 = 0;

  public fun get_message(message_owner : address) : string::String acquires Message {
    assert!(exists<Message>(message_owner), error::not_found(ENO_MESSAGE));
    borrow_global<Message>(message_owner).message
  }

  public entry fun set_message(admin: &signer, message : String) acquires Message{
    let message_owner_address = signer::address_of(admin);
    if (exists<Message>(message_owner_address)) {
      let stored_message = borrow_global_mut<Message>(message_owner_address);
      stored_message.message = message;
    } else {
      move_to(admin, Message{
        message_counter : 1,
        message : message
      });
    }
  }

#[test(account = @ownerAddress)]
  public entry fun test_message(account: &signer) acquires Message {
    let message = utf8(b"Hello, World!");
    set_message(account, message);
    let message_owner_address = signer::address_of(account);
    let stored_message = get_message(message_owner_address);
    assert!(message == stored_message, 0);
  }
}

코드를 살펴보면 set_message에서 로직이 이상한 부분을 찾을 수 있습니다. 바로 기존 메시지를 사용자가 새로 입력한 메시지로 변경하는 경우, 메시지는 정상적으로 변경되나 message_counter가 증가하지 않는다는 점입니다. 이를 해결하기 위해 Module을 업데이트 하도록 하겠습니다.


Step 1. Module의 함수 변경

기존의 Module에 있는 set_message 함수는 사용자가 입력한 message만 변경하고 message_counter는 변경하지 않는 로직이었습니다.

public entry fun set_message(admin: &signer, message : String) acquires Message{
  let message_owner_address = signer::address_of(admin);
  if (exists<Message>(message_owner_address)) {
    let stored_message = borrow_global_mut<Message>(message_owner_address);
    stored_message.message = message;
  } else {
    move_to(admin, Message{
      message_counter : 1,
      message : message
    });
  }
}

이러한 로직을 수정한 새로운 set_message 함수를 작성합니다. 이제 메시지를 변경할 때 마다 message_counter 역시 1 증가합니다.

  public entry fun set_message_with_message_counter(admin: &signer, message : String) acquires Message{
    let message_owner_address = signer::address_of(admin);
    if (exists<Message>(message_owner_address)) {
      let stored_message = borrow_global_mut<Message>(message_owner_address);
      // 표시
      stored_message.message_counter = stored_message.message_counter+1;
      stored_message.message = message;

    } else {
      move_to(admin, Message{
        message_counter : 1,
        message : message
      });
    }
  }

그리고 사용자가 혼선을 겪지 않도록 기존의 set_message 함수에 #[deprecated] 코드를 작성하여 변경되었음을 알립니다.

  #[deprecated]
  public entry fun set_message(admin: &signer, message : String) acquires Message{
    let message_owner_address = signer::address_of(admin);
    if (exists<Message>(message_owner_address)) {
      let stored_message = borrow_global_mut<Message>(message_owner_address);
      stored_message.message = message;
    } else {
      move_to(admin, Message{
        message_counter : 1,
        message : message
      });
    }
  }

전체적인 코드는 다음과 같습니다.

module ownerAddress::message {
  use std::signer;
  use std::string::{Self,utf8, String};
  use std::error;

  struct Message has key {
    message_counter : u64,
    message : String,
  }

  const ENO_MESSAGE: u64 = 0;

  public fun get_message(message_owner : address) : string::String acquires Message {
    assert!(exists<Message>(message_owner), error::not_found(ENO_MESSAGE));
    borrow_global<Message>(message_owner).message
  }

  #[deprecated]
  public entry fun set_message(admin: &signer, message : String) acquires Message{
    let message_owner_address = signer::address_of(admin);
    if (exists<Message>(message_owner_address)) {
      let stored_message = borrow_global_mut<Message>(message_owner_address);
      stored_message.message = message;
    } else {
      move_to(admin, Message{
        message_counter : 1,
        message : message
      });
    }
  }
  
  public entry fun set_message_with_message_counter(admin: &signer, message : String) acquires Message{
    let message_owner_address = signer::address_of(admin);
    if (exists<Message>(message_owner_address)) {
      let stored_message = borrow_global_mut<Message>(message_owner_address);
      stored_message.message_counter = stored_message.message_counter+1;
      stored_message.message = message;

    } else {
      move_to(admin, Message{
        message_counter : 1,
        message : message
      });
    }
  }

#[test(account = @ownerAddress)]
  public entry fun test_message(account: &signer) acquires Message {
    let message = utf8(b"Hello, World!");
    set_message_with_counter(account, message);
    let message_owner_address = signer::address_of(account);
    let stored_message = get_message(message_owner_address);
    assert!(message == stored_message, 0);
  }
}

Step 2. Compile 및 배포

코드 변경을 완료하였다면 터미널에 아래 명령어를 실행하여 정상적으로 컴파일이 되는지 확인합니다.

$ aptos move compile

정상적으로 컴파일이 완료되면 다음과 같은 응답을 받을 수 있습니다.

Compiling, may take a little while to download git dependencies...
UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-core.git
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING myFungibleAsset
{
  "Result": [
    "<your_account_address>::<your_module_name>"
  ]
}

컴파일 완료 후 터미널에 아래 명령어를 실행하여 Aptos 네트워크에 변경된 Module을 배포합니다.

  • 배포 시 owner의 Account가 기존의 owner와 다를 경우 신규 모듈로 배포가 될 수 있습니다.
  • 기존의 함수가 지워지거나 변경될 경우 배포에 실패할 수 있습니다.
  • Move.toml, module name 등 기존 설정이 변경된 경우 배포에 실패할 수 있습니다.
$ aptos move publish

정상적으로 배포가 되었다면 아래와 같이 트랜잭션에 대한 결과를 확인할 수 있습니다.

{
  "Result": {
    "transaction_hash": "<Your_transaction_hash>",
    "gas_used": 165,
    "gas_unit_price": 150,
    "sender": "<Your_Account_Address>",
    "sequence_number": 2,
    "success": true,
    "timestamp_us": 1720774008922260,
    "version": 5066304958,
    "vm_status": "Executed successfully"
  }
}

Step 3. 변경된 module 확인

Nodit에서 제공하는 Aptos Node API의 Get account module API를 이용하여 배포된 Module의 ABI를 확인합니다.

curl --request GET \
     --url https://aptos-testnet.nodit.io/v1/accounts/<your_account_address>/module/<your_module_name> \
     --header 'X-API-KEY: <your X-API-KEY>' \
     --header 'accept: application/json'

다음과 같이 Module에 수정하여 배포한 set_message_with_message_counter 함수에 대한 값을 찾을 수 있나요?

...
{
	name:"set_message_with_message_counter"
	visibility:"public"
	is_entry:true
	is_view:false
	generic_type_params:[]
	params:[
		0:"&signer"
		1:"0x1::string::String"
	]
	return:[]
}
...

  • 확인이 되지 않는다면 위의 코드와 다른 부분이 있는지 확인해 보세요.
  • 위의 코드와 차이가 없는데 되지 않나요? 여기[QnA 링크]를 클릭하여 QnA로 남겨주세요!

📘

Aptos는 매우 빠르게 업데이트 되고 있습니다!

Aptos 재단에서 배포한 SDK 버전에 따라 변경되는 점이 있을 수 있습니다. Nodit은 항상 이를 확인하고 있으나 시점에 따라 코드 변경으로 인한 오류가 발생할 수 있습니다.