Terra Network SmartContract Migration
Tags: terremoney, coswasm
Published: 2021-10-31 14:50:45

Terra network มีอัพเดทครั้งใหญ่ เพื่อเพิ่มประสิทธิภาพของ Network แต่ในบทความนี้ ผมจะไม่พูดเรื่อง Network แต่จะไป focus เรื่อง Smart Contract แทน โดยถ้าอยากรู้ว่ามีอะไรเปลี่ยนไปบ้าง ลองดูได้ลิ้งนี้แทน

https://medium.com/terra-money/columbus-5-launches-welcome-to-the-future-of-terra-8a9ebfa570c5

หนึ่งในการอัพเดทคือการ Bump cosmwasm ไปเป็น version 0.16 ( ซึ่ง breaking change กระจาย ) เรามาดูกันว่า เราต้องเปลี่ยนอะไรบ้างจาก contract version เดิม ( 0.10 ) 

นอกจากนี้ feature ที่เพิ่มมาก็เยอะมาก เพราะมัน upgrade กันข้ามมา 6 version ถ้าต้องการไล่อ่าน ตามไปที่ https://github.com/CosmWasm/cosmwasm/blob/main/CHANGELOG.md ได้เลย

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ex4435aqwh78d0niwzka.png)
> ของ Avareum protocol พังไป 240 errors 😱

## มีอะไรเปลี่ยนไปบ้าง?
- API มีความกระชับขึ้น Type ต่างๆ Generic ต่างๆใช้ง่ายขึ้น
- Support การทำ Migration ผ่าน Contract ได้แล้ว

โดยบทความนี้จะยกเอาแค่ Major change หลักๆ ที่ทีม Avareum ต้อง Migrate ซึ่งทางทีมก็ยังใช้ Feature ของมันไม่หมด (เยอะเกิน)

<hr />

## Support On-chain Contract Migration 
อันนี้คือที่รอคอยมานาน ซึ่งพึ่งมาเพิ่มใน coswasm 0.14 ทำให้ Contract สามารถ Migrate กันเองได้แบบ On-chain 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lwugxnyhc8up2xadehxo.png)

โดยที่ก่อนหน้านี้ จะจำกัดเรื่องการ migrate contract สามารถทำได้โดยผ่าน Off Chain API อย่างเดียว ทำให้ต้องเป็นหน้าที่ของ Owner ของ Contract เท่านั้นที่สามารถ Migrate ได้และหากมีหลาย Contract ก็ต้องทำทีละอันจาก Off chain

จาก Feature นี้ทำให้เราสามารถ Deploy contract set ที่มี Logic ในการ ทำ Migration ออกมาและสั่ง Migrate ที่เดียวจาก Off chain จาก Entrypoint ที่เดียว

```rust
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
    let cfg = query_config(&deps.as_ref())?;
    Ok(Response::new().add_messages(vec![
        CosmosMsg::Wasm(WasmMsg::Migrate {
            contract_addr: cfg.executor_mirror_address.into(),
            new_code_id: msg.sub_contract.executor_mirror_code_id,
            msg: to_binary(&msg.sub_contract.executor_mirror_migrate_msg)?,
        }),
        CosmosMsg::Wasm(WasmMsg::Migrate {
            contract_addr: cfg.executor_anchor_address.into(),
            new_code_id: msg.sub_contract.executor_anchor_code_id,
            msg: to_binary(&msg.sub_contract.executor_anchor_migrate_msg)?,
        }),
        CosmosMsg::Wasm(WasmMsg::Migrate {
            contract_addr: cfg.executor_terraswap_address.into(),
            new_code_id: msg.sub_contract.executor_terraswap_code_id,
            msg: to_binary(&msg.sub_contract.executor_terraswap_migrate_msg)?,
        }),
    ]))
}
```

<hr />

### วิธี binding Entrypoint เปลี่ยนใหม่
จากปกติก่อนหน้านี้ การกำหนด entrypoint ทั้งหมดจะอยู่ที่ `lib.rs` ให้ลบตรงนั้นทิ้งออกให้หมดแล้วไปใช้ macro กับ function ชื่อ `entry_point` แทน
พร้อมกับ เปลี่ยนชื่อ `init` เป็น `instantiate` และ `handle` เป็น `execute`

https://github.com/CosmWasm/cosmwasm/blob/main/CHANGELOG.md#changed-6

```rust

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    msg: InitMsg,
) -> Result<Response, ContractError> 
....


#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {

...


```


<hr />

### Extern API เปลี่ยนเป็น Deps และ DepsMut

จากการใช้งาน `Extern` กับ Generic type ทาง cosmwasm ได้เปลี่ยนมาเป็น type alias ทำให้ ปกติที่ใช้ `Extern` ให้เปลี่ยนเป็น `Deps` และ `mut Extern` เปลี่ยนเป็น `DepsMut` 
[Reference](https://github.com/CosmWasm/cosmwasm/blob/main/MIGRATING.md#011---012)
#### ตัวอย่าง
จาก
```rust
pub fn init<S: Storage, A: Api, Q: Querier>(
    deps: &mut Extern<S, A, Q>,
    _env: Env,
    msg: InitMsg,
) -> StdResult<InitResponse> {
```
เปลี่ยนเป็น
```rust
pub fn init(
    deps: DepsMut,
    _env: Env,
    msg: InitMsg,
) -> StdResult<InitResponse> {
```

จากอันนี้ การ access funds ที่ส่งมาจะเปลี่ยนจาก access ที่ env ไปผ่านตัว info แทน

<hr />

### เพิ่ม Arguments ให้กับ Contract entry point ( init, handle, migrate )
Type `Env` ถูกนำเอา sender ออกและย้ายไปไว้ใน Type ชื่อ `MessageInfo` เข้าใจว่าเพื่อให้เป็นระเบียบมากขึ้นเฉยๆ

#### ตัวอย่าง
จาก
```rust
pub fn handle(
    deps: DepsMut,
    env: Env,
    msg: HandleMsg,
) -> StdResult<HandleResponse> {
.....
    let senderAddr = env.message.sender;
```
เปลี่ยนเป็น
```rust
pub fn handle(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: HandleMsg,
) -> StdResult<HandleResponse> {
.....
    let senderAddr = info.sender;
```

<hr />

### Migrate Response, HandleResponse, InitResponse กลายเป็น Response เฉยๆ และเป็น non_exhaustive 
มีการรวม Response ต่างๆเป็นอันเดียว และใช้ Higher order function ในการ assign parameter ต่างๆให้กับ Response ( ไม่อนุญาตให้สร้าง Response แบบ struct expression แล้ว

ซึ่งผมชอบนะอันนี้ ดูกระชับขึ้นเยอะเลย ( ถึงจะต้องแก้ Contract เก่าเยอะหน่อย )

จาก
```rust
use cosmwasm_std::{ HandleResponse };

.....

 Ok(HandleResponse {
        messages: msgs,
        data: Option::None,
        attributes: logs,
    });
```
เปลี่ยนเป็น
```rust
use cosmwasm_std::{ Response };

.....

    Ok(Response::new()
        .add_attributes(logs)
        .add_messages(msgs))

```

<hr />

#### Log API เปลี่ยนชื่อเป็น Attribute
version 0.16 เอา log ออกหมดเลย เปลี่ยนไปเรียกว่า Attribute แทน รวมถึงการ return log จาก response ก็จะเปลี่ยนไปใช้ `add_attributes` หรือ `add_attribute` จาก Response แทน ( เพราะว่า `HandleResponse` ถูก Deprecated ไปแล้วจากด้านบน

```rust
-- let logs: Vec<LogAttribute> = vec![log("label","value")];
++ let attrs: Vec<Attribute> = vec![attr("label", "value")];

-- HandleResponse { log: logs }
++ Response::new().add_attributes(attrs);

```

<hr />

### HumanAddr ถูก Deprecated
ใน API version ใหม่จะมี `Addr` มาแทน `HumanAddr` โดยที่ API ส่วนใหญ่ ที่ใช้ `HumanAddr` จะเปลี่ยนไปใช้ `String` แทนแล้ว

เช่น `WasmMsg:Execute` จากปรกติ รับ `contarct_addr` เป็น `HumanAddr` ตอนนี้จะรับเป็น `String` แทน ซึ่งหากเราตัดสินใจใช้ `Addr` ก็จะมี `to_string` ให้ใช้เปลี่ยนจาก `Addr` เป็น `String`

```rust
-- let contract_addr: HumanAddr;
++ let contract_addr: Addr;
WasmMsg::Execute {
   -- contract_addr: contract_addr,
   ++ contract_addr: contract_addr.to_string(),
}
```


หลักๆเราต้อง Migrate ตัว contract ประมานนี้ ส่วนใหญ่ยังไม่มีอะไรที่ Breaking Change ถึงกับขนาดต้องออกแบบ Contract ใหม่

ส่วนอื่นๆ นอกจากนี้ลึกๆ ตามไปไล่อ่านที่นี่ได้เลยจ้า
https://github.com/CosmWasm/cosmwasm/blob/main/MIGRATING.md