Terra Network SmartContract Migration
Rungsikorn Rungsikavanich
Posted on October 31, 2021
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 ได้เลย
ของ Avareum protocol พังไป 240 errors 😱
มีอะไรเปลี่ยนไปบ้าง?
- API มีความกระชับขึ้น Type ต่างๆ Generic ต่างๆใช้ง่ายขึ้น
- Support การทำ Migration ผ่าน Contract ได้แล้ว
โดยบทความนี้จะยกเอาแค่ Major change หลักๆ ที่ทีม Avareum ต้อง Migrate ซึ่งทางทีมก็ยังใช้ Feature ของมันไม่หมด (เยอะเกิน)
Support On-chain Contract Migration
อันนี้คือที่รอคอยมานาน ซึ่งพึ่งมาเพิ่มใน coswasm 0.14 ทำให้ Contract สามารถ Migrate กันเองได้แบบ On-chain
โดยที่ก่อนหน้านี้ จะจำกัดเรื่องการ migrate contract สามารถทำได้โดยผ่าน Off Chain API อย่างเดียว ทำให้ต้องเป็นหน้าที่ของ Owner ของ Contract เท่านั้นที่สามารถ Migrate ได้และหากมีหลาย Contract ก็ต้องทำทีละอันจาก Off chain
จาก Feature นี้ทำให้เราสามารถ Deploy contract set ที่มี Logic ในการ ทำ Migration ออกมาและสั่ง Migrate ที่เดียวจาก Off chain จาก Entrypoint ที่เดียว
#[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)?,
}),
]))
}
วิธี binding Entrypoint เปลี่ยนใหม่
จากปกติก่อนหน้านี้ การกำหนด entrypoint ทั้งหมดจะอยู่ที่ lib.rs
ให้ลบตรงนั้นทิ้งออกให้หมดแล้วไปใช้ macro กับ function ชื่อ entry_point
แทน
พร้อมกับ เปลี่ยนชื่อ init
เป็น instantiate
และ handle
เป็น execute
https://github.com/CosmWasm/cosmwasm/blob/main/CHANGELOG.md#changed-6
#[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> {
...
Extern API เปลี่ยนเป็น Deps และ DepsMut
จากการใช้งาน Extern
กับ Generic type ทาง cosmwasm ได้เปลี่ยนมาเป็น type alias ทำให้ ปกติที่ใช้ Extern
ให้เปลี่ยนเป็น Deps
และ mut Extern
เปลี่ยนเป็น DepsMut
Reference
ตัวอย่าง
จาก
pub fn init<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
_env: Env,
msg: InitMsg,
) -> StdResult<InitResponse> {
เปลี่ยนเป็น
pub fn init(
deps: DepsMut,
_env: Env,
msg: InitMsg,
) -> StdResult<InitResponse> {
จากอันนี้ การ access funds ที่ส่งมาจะเปลี่ยนจาก access ที่ env ไปผ่านตัว info แทน
เพิ่ม Arguments ให้กับ Contract entry point ( init, handle, migrate )
Type Env
ถูกนำเอา sender ออกและย้ายไปไว้ใน Type ชื่อ MessageInfo
เข้าใจว่าเพื่อให้เป็นระเบียบมากขึ้นเฉยๆ
ตัวอย่าง
จาก
pub fn handle(
deps: DepsMut,
env: Env,
msg: HandleMsg,
) -> StdResult<HandleResponse> {
.....
let senderAddr = env.message.sender;
เปลี่ยนเป็น
pub fn handle(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: HandleMsg,
) -> StdResult<HandleResponse> {
.....
let senderAddr = info.sender;
Migrate Response, HandleResponse, InitResponse กลายเป็น Response เฉยๆ และเป็น non_exhaustive
มีการรวม Response ต่างๆเป็นอันเดียว และใช้ Higher order function ในการ assign parameter ต่างๆให้กับ Response ( ไม่อนุญาตให้สร้าง Response แบบ struct expression แล้ว
ซึ่งผมชอบนะอันนี้ ดูกระชับขึ้นเยอะเลย ( ถึงจะต้องแก้ Contract เก่าเยอะหน่อย )
จาก
use cosmwasm_std::{ HandleResponse };
.....
Ok(HandleResponse {
messages: msgs,
data: Option::None,
attributes: logs,
});
เปลี่ยนเป็น
use cosmwasm_std::{ Response };
.....
Ok(Response::new()
.add_attributes(logs)
.add_messages(msgs))
Log API เปลี่ยนชื่อเป็น Attribute
version 0.16 เอา log ออกหมดเลย เปลี่ยนไปเรียกว่า Attribute แทน รวมถึงการ return log จาก response ก็จะเปลี่ยนไปใช้ add_attributes
หรือ add_attribute
จาก Response แทน ( เพราะว่า HandleResponse
ถูก Deprecated ไปแล้วจากด้านบน
-- let logs: Vec<LogAttribute> = vec![log("label","value")];
++ let attrs: Vec<Attribute> = vec![attr("label", "value")];
-- HandleResponse { log: logs }
++ Response::new().add_attributes(attrs);
HumanAddr ถูก Deprecated
ใน API version ใหม่จะมี Addr
มาแทน HumanAddr
โดยที่ API ส่วนใหญ่ ที่ใช้ HumanAddr
จะเปลี่ยนไปใช้ String
แทนแล้ว
เช่น WasmMsg:Execute
จากปรกติ รับ contarct_addr
เป็น HumanAddr
ตอนนี้จะรับเป็น String
แทน ซึ่งหากเราตัดสินใจใช้ Addr
ก็จะมี to_string
ให้ใช้เปลี่ยนจาก Addr
เป็น String
-- 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
Posted on October 31, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.