Sui V0.28 + V0.29版本迁移指南
Sui V0.28 + V0.29版本引入一些不兼容更新,可能需要更新你的应用或调整程序,才能正常运行。本指南提供了迁移步骤来帮助更新,同时也将介绍新版本中一些新增功能。
Sui Move 更新
本节将介绍有关Sui Framework相关的更新。
Sui Framework拆分成了两个包#
更新时间2023年3月27日
sui-framework包含了Sui Move中object、 transfer和dynamic_field等核心包。在Sui上开发的合约常常依赖sui-framework包。此前,sui-framework包含一个governance文件夹,里面定义了一些与Sui系统运行相关的模块,比如validator_set和staking_pool。这模块与framework内的其他模块有根本区别,开发人员不常用。为了简化,此版本将sui-framework拆分为两个,以提高模块化、可用性和升级性。
在PR 9618中,sui-framework在packages目录中包含3个包:sui-system、sui-framework和move-stdlib:
- sui-system中包含sui-framework/sources/governance目录中的模块, 其中所有验证节点管理和质押相关函数,都发布在以0x3命名的sui_system地址上。
- sui-framework包含不在governance文件夹中的所有其他模块,该framework为开发人员提供标准库和工具类模块,它仍然在0x2命名的sui地址上。
- move-stdlib包含以前位于sui-framework/deps文件夹中,它仍然在0x1 命名的std地址上。
如果您依赖sui-framework开发代码库,包管理配置文件Move.toml必须更改路径地址:
发布V0.28版本之前
[package]
name = “Example”
version = “0.0.1”
published-at = “0x42”
[dependencies]
Sui = { git = “https://github.com/MystenLabs/sui.git", subdir=”crates/sui-framework/”, rev = “devnet” }
[addresses]
example = “0x42”
发布V0.28版本之后
[package]
name = “Example”
version = “0.0.1”
published-at = “0x42”
[dependencies]
Sui = { git = “https://github.com/MystenLabs/sui.git", subdir=”crates/sui-framework/packages/sui-framework/”, rev = “devnet” }
[addresses]
example = “0x42”
就是subdir字段修改成crates/sui-framework/packages/sui-framework/
如果您的代码使用governance文件夹中如下的模块:
genesis.move、sui_system.move、validator_cap.move、voting_power.move、stake_subsidy.move、sui_system_state_inner.move、validator_set.move、staking_pool.move、validator.move或者validator_wrapper.move
这些模块现在位于sui-system包中,您必须将SuiSystem列为依赖项,并通过0x3或sui_system命名地址来访问它们。
ecdsa_k1 ecrecover和secp256k1_verify#
在这个版本中,ecdsa_k1::ecrecover和ecdsa_k1::secp256k1_verify要求用户输入原始信息而不是哈希后的信息
- ecdsa_k1::ecrecover(sig, hashed_msg, hash_function) ,升级成:ecdsa_k1::secp256k1_ecrecover(sig, msg, hash_function)
- ecdsa_k1::secp256k1_verify(sig, pk, hashed_msg),升级成:ecdsa_k1::secp256k1_verify(sig, pk, msg, hash_function)
当您调用这些 API 时,必须提供原始信息而不是哈希后的信息用于verify或ecrevover。 您还必须提供由u8数组表示的hash_function名称。 有关详细信息,请参阅源代码:
ID不能构造传入#
当您建立一个object时,UID必须使用未被注册过的,也就是UID必须来自object::new(或用于测试的test_scenario::new_object)。 想要迁移已有的项目,之前将UID作为参数来构造object的任何函数现需要用TxContext来生成新的ID。
例如,在V0.28之前的版本中,创建一个object代码:
fun new(id: UID): Counter {
Counter { id, count: 0 }
}
而在V0.28版本中,创建一个object代码:
fun new(ctx: &mut TxContext): Counter {
Counter { id: object::new(ctx), count: 0 }
}
Publisher#
Publisher增加了一个重要的补充性功能,即Publisher object现在需要的OTW(一次性见证者),可以通过sui::package::claim调用在package内的任何模块来实现:
module example::dummy {
use sui::package;
use sui::tx_context::TxContext;
struct DUMMY has drop {}
fun init(otw: DUMMY, ctx: &mut TxContext) {
// creates a Publisher object and sends to the `sender`
package::claim_and_keep(otw, ctx)
}
}
要了解更多有关Publisher的信息,请参阅Publisher。
Sui Object Display 标准#
这个版本包含了Sui Object Display 标准,是一种使用通用模板来描述类型object的新方式,使其链下展示更标准,RPC API已经支持该新标准。
想要获取更详细的描述和标准制定的动机,请参阅Sui Object Display提案。
在Sui Move中, 可以通过调用display::new<T>(&Publisher)来claim Display object。函数签名中规定,需要用到Publisher object。Display claim后,可以通过向其添加新字段(模板)来修改Display。当object准备好发布时,需要调用display::update_version(&mut Display)来发布并使其可用,每当添加/编辑Display应当再次调用update_version来声明已经更新。
我们建议在Display中使用的字段如下:
- name:显示的名称
- link:对象的app链接/外部链接
- description:描述信息
- image_url:图片的URL或者blob类型的图片
- project_url:网站的链接
- creator:以任何方式提及创作者(文本、链接、地址等)
请在Display中查阅其他信息和示例。
API和SDK更新#
本节将介绍Sui API和SDK更新后的相关内容。
读取objects#
sui_getObject端点现在采用一种附加配置参数的方法SuiObjectDataOptions来控制端点检索字段的类型。默认情况下,端点仅检索object的引用,除非客户端请求时明确指定其他数据,例如type、owner或者bcs数据类型。
TypeScript迁移#
import { JsonRpcProvider } from “@mysten/sui.js”;
const provider = new JsonRpcProvider();
// Prior to release .28
const txn = await provider.getObject(
“0xcff6ccc8707aa517b4f1b95750a2a8c666012df3”
);
const txns = await provider.getObjectBatch([
“0xcff6ccc8707aa517b4f1b95750a2a8c666012df3”,
“0xdff6ccc8707aa517b4f1b95750a2a8c666012df3”,
]);
// Updated for release .28
const txn = await provider.getObject({
id: “0xcff6ccc8707aa517b4f1b95750a2a8c666012df3”,
// fetch the object content field and display
options: {
showContent: true,
showDisplay: true,
},
});
const txns = await provider.multiGetObjects({
ids: [
“0xcff6ccc8707aa517b4f1b95750a2a8c666012df3”,
“0xdff6ccc8707aa517b4f1b95750a2a8c666012df3”,
],
// only fetch the object type
options: { showType: true },
});
JSON RPC迁移#
# Prior to release .28
curl — location — request POST ‘https://fullnode.devnet.sui.io:443' \
— header ‘Content-Type: application/json’ \
— data-raw ‘{
“jsonrpc”: “2.0”,
“id”: 1,
“method”: “sui_getObject”,
“params”: {
“object_id”: “0x08240661f5504c9bb4a487d9a28e7e9d6822abf692801f2a750d67a44d0b2340”,
}
}’
# Updated for release .28
curl — location — request POST ‘https://fullnode.devnet.sui.io:443' \
— header ‘Content-Type: application/json’ \
— data-raw ‘{
“jsonrpc”: “2.0”,
“id”: 1,
“method”: “sui_getObject”,
“params”: {
“object_id”: “0x08240661f5504c9bb4a487d9a28e7e9d6822abf692801f2a750d67a44d0b2340”,
“options”: {
“showContent”: true,
“showOwner”: true,
}
}
}’
# If you use sui_getRawObject, enable the showBcs option to retrieve it
curl — location — request POST ‘https://fullnode.devnet.sui.io:443' \
— header ‘Content-Type: application/json’ \
— data-raw ‘{
“jsonrpc”: “2.0”,
“id”: 1,
“method”: “sui_getObject”,
“params”: {
“object_id”: “0x08240661f5504c9bb4a487d9a28e7e9d6822abf692801f2a750d67a44d0b2340”,
“options”: {
“showBcs”: true
}
}
}’
Display#
要获取object的Display ,请将附加标志传递给sui_getObject调用。
{
showDisplay: true;
}
返回值是某个类型的处理模板,例如,对于Sui Capys,它可以为:
{
“name”: “Capy — one of many”,
“description”: “Join our Capy adventure”,
“link”: “https://capy.art/capy/0x00000000....",
“image_url”: “https://api.capy.art/capys/0x000adadada..../svg",
“project_url”: “https://capy.art/",
“creator”: “Capybara Lovers”
}
读交易#
sui_getTransactionBlock和sui_multiGetTransaction函数现在新增一个可选参数options。使用options指定要检索的字段,例如交易、变动或事件。 默认情况下,它只返回交易签名。
import { JsonRpcProvider } from “@mysten/sui.js”;
const provider = new JsonRpcProvider();
// Prior to release .28
const provider = new JsonRpcProvider();
const txn = await provider.getTransactionWithEffects(
“6mn5W1CczLwitHCO9OIUbqirNrQ0cuKdyxaNe16SAME=”
);
// You can also fetch multiple transactions in one batch request
const txns = await provider.getTransactionWithEffectsBatch([
“6mn5W1CczLwitHCO9OIUbqirNrQ0cuKdyxaNe16SAME=”,
“7mn5W1CczLwitHCO9OIUbqirNrQ0cuKdyxaNe16SAME=”,
]);
// Updated for release .28
const provider = new JsonRpcProvider();
const txn = await provider.getTransactionBlock({
digest: “6mn5W1CczLwitHCO9OIUbqirNrQ0cuKdyxaNe16SAME=”,
// only fetch the effects field
options: { showEffects: true },
});
// You can also fetch multiple transactions in one batch request
const txns = await provider.multiGetTransactionBlocks({
digests: [
“6mn5W1CczLwitHCO9OIUbqirNrQ0cuKdyxaNe16SAME=”,
“7mn5W1CczLwitHCO9OIUbqirNrQ0cuKdyxaNe16SAME=”,
],
// fetch both the input transaction data as well as effects
options: { showInput: true, showEffects: true },
});
读事件#
此版本对读事件进行了以下更新:
- 删除系统事件,例如Publish、TransferObject、NewObject,只保留 MoveEvents
- 在SuiTransactionResponse中添加了object_changes和balance_changes字段
import { JsonRpcProvider } from “@mysten/sui.js”;
const provider = new JsonRpcProvider();
// Prior to release .28
provider.getEvents({ Sender: toolbox.address() }, null, 2);
// Updated for release .28
const events = provider.queryEvents({
query: { Sender: toolbox.address() },
limit: 2,
});
// Subscribe events
// Prior to release .28
const subscriptionId = await provider.subscribeEvent(
{ SenderAddress: “0xbff6ccc8707aa517b4f1b95750a2a8c666012df3” },
(event: SuiEventEnvelope) => {
// handle subscription notification message here. This function is called once per subscription message.
}
);
// later, to unsubscribe
// calls RPC method ‘sui_unsubscribeEvent’ with params: [ subscriptionId ]
const subFoundAndRemoved = await provider.unsubscribeEvent(subscriptionId);
// Updated for release .28
// calls RPC method ‘sui_subscribeEvent’ with params:
// [ { Sender: ‘0xbff6ccc8707aa517b4f1b95750a2a8c666012df3’ } ]
const subscriptionId = await provider.subscribeEvent({
filter: { Sender: “0xbff6ccc8707aa517b4f1b95750a2a8c666012df3” },
onMessage(event: SuiEvent) {
// handle subscription notification message here. This function is called once per subscription message.
},
});
// later, to unsubscribe
// calls RPC method ‘sui_unsubscribeEvent’ with params: [ subscriptionId ]
const subFoundAndRemoved = await provider.unsubscribeEvent({
id: subscriptionId,
});
分页#
此版本更新了分页的定义。
发布V0.28版本之前
pub struct Page<T, C> {
pub data: Vec<T>,
pub next_cursor: Option<C>,
}
发布V0.28版本之后
pub struct Page<T, C> {
pub data: Vec<T>,
pub next_cursor: Option<C>,
pub has_next_page: bool,
}
备注:next_cursor现在不会有Null值了,如果data不为空,它总是指向最后一项数据;要检查当前页面是否是最后一页,现在您可以简单地使用has_next_page,而不是执行next_cursor.is_none()。
如果使用Page是一页一页的读取,现在读取到最新一页时,无需手动处理next_cursor返回的None值,而是可以一直使用返回的next_cursor作为读取下一页的输入参数。在此版本之前,读取过程将从头开始直到结束,并且没有正确处理None值。
构建和执行交易#
Signer和SignableTransaction接口之前的transaction builder方法已被删除,并替换为新的Transaction builder类。这个新的transaction builder充分利用了Programmable Transactions优点。
// Construct a new transaction:
const tx = new Transaction();
// Example replacement for a SUI token transfer:
const [coin] = tx.splitCoins(tx.gas, [tx.pure(1000)]);
tx.transferObjects([coin], tx.pure(keypair.getPublicKey().toSuiAddress()));
// Merge a list of coins into a primary coin:
tx.mergeCoin(tx.object(“0xcoinA”), [
tx.object(“0xcoinB”),
tx.object(“0xcoinC”),
]);
// Make a move call:
tx.moveCall({
target: `${packageObjectId}::nft::mint`,
arguments: [tx.pure(“Example NFT”)],
});
// Execute a transaction:
const result = await signer.signAndExecuteTransaction({ transaction: tx });
交易现在支持列表中提供的硬币作为交易时gas的支付。 默认情况下,transaction builder会自动计算gas和选择硬币用于支付。当然你还可以选择设置这些值,例如设置预算、更改gas价格或gas支付选择:
// Set an explicit gas price. By default, uses the current reference gas price:
tx.setGasPrice(100);
// Change the gas budget (in SUI). By default, this executes a dry run and uses the gas consumed from that as the budget.
tx.setGasBudget(customBudgetDefined);
// Set the vector of gas objects to use as the gas payment.
tx.setGasPayment([coin1, coin2]);
质押更新#
本节将介绍与Sui质押更新相关的内容。
解除用于质押锁定的 Coin#
此版本之前,用户可以将其Coin<SUI>或LockedCoin<SUI>质押给验证节点,此版本更新后,取消了对用于质押锁定Coin的支持,因此质押功能现只支持Coin<SUI>。
更新质押Sui object布局#
在此版本之前,质押的Sui结构具有以下定义:
struct StakedSui has key {
id: UID,
/// The validator we are staking with.
validator_address: address,
/// The epoch at which the staking pool started operating.
pool_starting_epoch: u64,
/// The epoch at which the delegation is requested.
delegation_request_epoch: u64,
/// The staked SUI tokens.
principal: Balance<SUI>,
/// If the stake comes from a Coin<SUI>, this field is None. If it comes from a LockedCoin<SUI>, this
/// field will record the original lock expiration epoch, to be used when unstaking.
sui_token_lock: Option<EpochTimeLock>,
}
随着对用于质押锁定Coin的移除以及Sui质押流程的改变,新的结构定义为:
struct StakedSui has key {
id: UID,
/// ID of the staking pool we are staking with.
pool_id: ID,
/// The validator we are staking with.
validator_address: address,
/// The epoch at which the stake becomes active.
stake_activation_epoch: u64,
/// The staked SUI tokens.
principal: Balance<SUI>,
}
存入/取出质押API的更新#
此版本包括以下与存入/取出质押请求相关的更新:
- 移除request_switch_delegation函数
- 重命名所有委托功能以使用“质押”而不是“委托”
在V0.28版本之前,函数名称为:
/// Add delegated stake to a validator’s staking pool using multiple coins and amount.
#[method(name = “requestAddDelegation”)]
async fn request_add_delegation(
&self,
/// the transaction signer’s Sui address
signer: SuiAddress,
/// Coin<SUI> or LockedCoin<SUI> object to delegate
coins: Vec<ObjectID>,
/// delegation amount
amount: Option<u64>,
/// the validator’s Sui address
validator: SuiAddress,
/// gas object to be used in this transaction, node will pick one from the signer’s possession if not provided
gas: Option<ObjectID>,
/// the gas budget, the transaction will fail if the gas cost exceed the budget
gas_budget: u64,
) -> RpcResult<TransactionBlockBytes>;
/// Withdraw a delegation from a validator’s staking pool.
#[method(name = “requestWithdrawDelegation”)]
async fn request_withdraw_delegation(
&self,
/// the transaction signer’s Sui address
signer: SuiAddress,
/// Delegation object ID
delegation: ObjectID,
/// StakedSui object ID
staked_sui: ObjectID,
/// gas object to be used in this transaction, node will pick one from the signer’s possession if not provided
gas: Option<ObjectID>,
/// the gas budget, the transaction will fail if the gas cost exceed the budget
gas_budget: u64,
) -> RpcResult<TransactionBlockBytes>;
/// Switch delegation from the current validator to a new one.
#[method(name = “requestSwitchDelegation”)]
async fn request_switch_delegation(
&self,
/// the transaction signer’s Sui address
signer: SuiAddress,
/// Delegation object ID
delegation: ObjectID,
/// StakedSui object ID
staked_sui: ObjectID,
/// Validator to switch to
new_validator_address: SuiAddress,
/// gas object to be used in this transaction, node will pick one from the signer’s possession if not provided
gas: Option<ObjectID>,
/// the gas budget, the transaction will fail if the gas cost exceed the budget
gas_budget: u64,
) -> RpcResult<TransactionBlockBytes>;
从V0.28版本开始生效,函数名称为:
/// Add stake to a validator’s staking pool using multiple coins and amount.
#[method(name = “requestAddStake”)]
async fn request_add_stake(
&self,
/// the transaction signer’s Sui address
signer: SuiAddress,
/// Coin<SUI> object to stake
coins: Vec<ObjectID>,
/// stake amount
amount: Option<u64>,
/// the validator’s Sui address
validator: SuiAddress,
/// gas object to be used in this transaction, node will pick one from the signer’s possession if not provided
gas: Option<ObjectID>,
/// the gas budget, the transaction will fail if the gas cost exceed the budget
gas_budget: u64,
) -> RpcResult<TransactionBlockBytes>;
/// Withdraw stake from a validator’s staking pool.
#[method(name = “requestWithdrawStake”)]
async fn request_withdraw_stake(
&self,
/// the transaction signer’s Sui address
signer: SuiAddress,
/// Delegation object ID
delegation: ObjectID,
/// StakedSui object ID
staked_sui: ObjectID,
/// gas object to be used in this transaction, node will pick one from the signer’s possession if not provided
gas: Option<ObjectID>,
/// the gas budget, the transaction will fail if the gas cost exceed the budget
gas_budget: u64,
) -> RpcResult<TransactionBlockBytes>;
getDelegatedStakes的更新#
委托质押的函数(getDelegatedStakes )函数已重命名为getStakes, getStakes函数返回验证节点按质押池分组地址的所有质押,以及到目前为止获得的预估质押奖励。
{
“jsonrpc”: “2.0”,
“result”: [
{
“validatorAddress”: “0x8760b337dcb641811414daff8f98e6824caf7e5ca28530c4248557057ddc9004”,
“stakingPool”: “0x628ffd0e51e9a6ea32c13c2739a31a8f344b557d3429e057b377a9c499b9bb13”,
“stakes”: [
{
“stakedSuiId”: “0xa3cc3319d355dc92afee3669cd8f545de98c5ee380b6e1275b891bebdd82ad28”,
“stakeRequestEpoch”: 3,
“stakeActiveEpoch”: 4,
“principal”: 99999999998977,
“status”: “Active”,
“estimatedReward”: 998
}
]
},
{
“validatorAddress”: “0x6b34f8d6d70676db526017b03ec35f8f74ec67ee10426e2a3049a42045c90913”,
“stakingPool”: “0xd5d9aa879b78dc1f516d71ab979189086eff752f65e4b0dea15829e3157962e1”,
“stakes”: [
{
“stakedSuiId”: “0x5ee438610276e8fcfe0c0615caf4a2ca7c408569f47c5a622be58863c35b357b”,
“stakeRequestEpoch”: 2,
“stakeActiveEpoch”: 3,
“principal”: 100000000000000,
“status”: “Active”,
“estimatedReward”: 1998
},
{
“stakedSuiId”: “0x9eac8bd615977f8d635dc2054d13c463829161be41425d701ba8f9a444ca69e9”,
“stakeRequestEpoch”: 5,
“stakeActiveEpoch”: 6,
“principal”: 100000000000000,
“status”: “Pending”
}
]
}
],
“id”: 1
}
增加getStakesByIds函数#
新的getStakesByIds函数,可以使用已质押的SUI IDs数组查询委托的质押。 该函数返回验证节点按质押池分组地址的所有质押,以及到目前为止获得的预估质押奖励。
Secp256k1导出密钥对#
在函数签名上将Secp256k1.deriveKeypair与Ed25519进行匹配,以接受助记词字符串和可选路径字符串,而不是必需的路径字符串和助记符字符串。有关详细信息,请参阅PR 8542。