+2

Tạo transaction với Sui Blockchain

Mayfest2023

Sui objects

Sui Blockchain hoạt động trên hệ thống Object Based, đây cũng là sự khác biệt lớn nhất so với các blockchain khác. Thay vì lưu lại balance theo account, Sui Blockchain sẽ hoạt động xung quanh các objects, mọi thứ trong Sui Blockchain đều là objects.

Ví dụ

Bạn có 2 object có type là coin::Coin<0x2::sui::SUI> và có thông số balance lần lượt của object thứ 1: 699206037, và object thứ 2 là: 145432704, vậy tổng cộng bạn có: 844638741, (0.8446Sui, coin Sui có chỉ số decimals là 9). Balance của bạn sẽ được cấu thành dựa trên tổng số balance nằm trong các object cùng type mà bạn muốn tính balance.

Và để quản lý, các object chắc chắn đều sẽ có object id của riêng bản thân nó.

Fact: coin::Coin<0x2::sui::SUI> cũng là object (có object id là 0x2) và chúng ta đang sử dụng module coin có lưu trữ Struct Coin với type là 0x2::sui::SUI (Coin<0x2::sui::SUI>)

Chuyển tiền

Thay vì ghi nợ và ghi có như cách hoạt động bình thường của ledger, để mô phỏng thế giới thật, Sui Blocckhain hoạt động dựa trên cơ chế merge, split (hợp nhất và phân tách).

Cụ thể, giống như việc bạn ra biển và múc lấy 1 nắm cát (objects) và bỏ vào hủ của bạn (account), để tính tổng số hạt cát mà bạn có, bạn phải đếm nó (calculate balance from adding balance in objects), sau khi đếm xong bạn đã nắm rõ số lượng cát trong hủ của mình.

Bạn có thể hợp nhất các hạt cát trong hủ thành 1 hạt cát to có balance bằng tổng balance của các hạt nhỏ.

Khi chuyển 1 nữa cát trong hủ, nếu bạn chưa hợp nhất, bạn có thể múc 1 nữa hủ để chuyển, nếu đã hợp nhất, bạn chia đôi hạt cát to để chuyển.

Tìm hiểu theo về object trong Sui tạI: https://docs.sui.io/learn/objects

Flow cơ bản

Transaction gửi Sui

Lấy ra tất cả Sui objects hiện đang có trong account

import { SUI_TYPE_ARG } from "@mysten/sui.js";

const suiCoins = await provider.getCoins({ owner: address, cursor: nextCursor || undefined, limit: 100, coinType: SUI_TYPE_ARG });

Để lấy tất cả các objects, hãy sử dụng loop với hasNextPage, nextCursor trong kết quả trả về của suiCoins cho đến khi hasNextPage === false.

(xem các tạo providersigner tại: https://viblo.asia/p/sui-blockchain-wallet-account-38X4EN9zJN2#_tao-signer-tu-keypair-va-provider-6)

Warning: nên kiểm tra tổng số objects hiện tại của coin type cần gửi trong account trước khi fetch lấy hết các objects (sử dụng getAllBalances với prop: coinObjectCount), vì nếu tổng số quá lớn thì có thể sẽ không thực hiện được tx (tối đa khoảng 500 objects/lần) . Hãy merge tổng số objects về nhỏ hơn 500 để thực hiện các tx 1 cách tốt nhất (công cụ hỗ trợ merge: https://fewcha.app/sui-merge-coin/)

Gửi 1 phần đến người nhận

const tx = new TransactionBlock();

tx.setGasPayment(suiCoins.map((coin) => ({ version: coin.version, digest: coin.digest, objectId: coin.coinObjectId }))); // lấy tất cả objects sui hiện tại

const [coinIn] = tx.splitCoins(tx.gas, [tx.pure(amount)]); // lấy phần cần chuyển
tx.transferObjects([coinIn], tx.pure(receiverAddress, "address")); // chuyển đến người nhận

const dryrun = await signer.dryRunTransactionBlock({ transactionBlock: tx });
// kiểm tra dryrun nếu bạn muốn
const submittedTx = await signer.signAndExecuteTransactionBlock({ transactionBlock: tx });
await provider.waitForTransactionBlock({ digest: submittedTx.digest });

Gửi tất cả Sui đến người nhận

const tx = new TransactionBlock();

tx.setGasPayment(suiCoins.map((coin) => ({ version: coin.version, digest: coin.digest, objectId: coin.coinObjectId })));
tx.transferObjects(tx.gas, tx.pure(receiverAddress, "address")); // chuyển tất cả sui đang có đến người nhận

const dryrun = await signer.dryRunTransactionBlock({ transactionBlock: tx });
const submittedTx = await signer.signAndExecuteTransactionBlock({ transactionBlock: tx });
await provider.waitForTransactionBlock({ digest: submittedTx.digest });

Để gửi Sui đến nhiều người nhận trong cùng 1 tx

Sử dụng công cụ Sui Bulk Send tại: https://fewcha.app/sui-merge-coin/

Transaction gửi coin

const tx = new TransactionBlock();

tx.setGasPayment(suiCoins.map((coin) => ({ version: coin.version, digest: coin.digest, objectId: coin.coinObjectId })));
const coinXs = await provider.getCoins({ owner: currentAddress, coinType: coinTypeX }); // nên lấy tất cả objects bằng loop qua hasNextPage và nextCursor
const [primaryCoinX, ...restCoinXs] = coinYs.data;
  
tx.mergeCoins(
  tx.object(primaryCoinX.coinObjectId),
  restCoinXs.map((coin) => tx.object(coin.coinObjectId)),
);

const [coinIn] = tx.splitCoins(tx.object(primaryCoinY.coinObjectId), [tx.pure(amount)]); // lấy phần cần chuyển
tx.transferObjects([coinIn], tx.pure(receiverAddress, "address"));

const dryrun = await signer.dryRunTransactionBlock({ transactionBlock: tx });
const submittedTx = await signer.signAndExecuteTransactionBlock({ transactionBlock: tx });
await provider.waitForTransactionBlock({ digest: submittedTx.digest });

Gửi tất cả balance của coin

const tx = new TransactionBlock();

tx.setGasPayment(suiCoins.map((coin) => ({ version: coin.version, digest: coin.digest, objectId: coin.coinObjectId })));
const coinXs = await provider.getCoins({ owner: currentAddress, coinType: coinTypeX }); // nên lấy tất cả objects bằng loop qua hasNextPage và nextCursor
const [primaryCoinX, ...restCoinXs] = coinYs.data;
  
tx.mergeCoins(
  tx.object(primaryCoinX.coinObjectId),
  restCoinXs.map((coin) => tx.object(coin.coinObjectId)),
);

tx.transferObjects([tx.object(primaryCoinX.coinObjectId)], tx.pure(receiverAddress, "address"));

const dryrun = await signer.dryRunTransactionBlock({ transactionBlock: tx });
const submittedTx = await signer.signAndExecuteTransactionBlock({ transactionBlock: tx });
await provider.waitForTransactionBlock({ digest: submittedTx.digest });

Gửi Sui và Coin khác trong cùng 1 transaction

const tx = new TransactionBlock();

tx.setGasPayment(suiCoins.map((coin) => ({ version: coin.version, digest: coin.digest, objectId: coin.coinObjectId })));

const coinXs = await provider.getCoins({ owner: currentAddress, coinType: coinTypeX });
const [primaryCoinX, ...restCoinXs] = coinYs.data;
  
tx.mergeCoins(
  tx.object(primaryCoinX.coinObjectId),
  restCoinXs.map((coin) => tx.object(coin.coinObjectId)),
);

const [suiCoinIn] = tx.splitCoins(tx.gas, [tx.pure(suiAmount)]); 
const [coinIn] = tx.splitCoins(tx.object(primaryCoinY.coinObjectId), [tx.pure(xAmount)]);


tx.transferObjects([suiCoinIn, coinIn], tx.pure(receiverAddress, "address"));

const dryrun = await signer.dryRunTransactionBlock({ transactionBlock: tx });
const submittedTx = await signer.signAndExecuteTransactionBlock({ transactionBlock: tx });
await provider.waitForTransactionBlock({ digest: submittedTx.digest });

Transaction gọi lên Smart Contract

Transaction buy suifrens

https://suifrens.com/

Để thực hiện việc mua NFT suifrens bằng script, chúng ta có thể tạo transaction như sau:

const tx = new TransactionBlock();

const coin = tx.splitCoins(tx.gas, [tx.pure(8000000000)]); // SUI
const rest = tx.moveCall({
  target: "0xee496a0cc04d06a345982ba6697c90c619020de9e274408c7819f787ff66e1a1::genesis::Mint",
  arguments: [
    tx.object("0x2ec5baa9b548b0dbf76f70063f74f95e2451fa409535b7cf755a829b167b4a58"),
    tx.object("0x0000000000000000000000000000000000000000000000000000000000000006"),
    coin,
  ],
  typeArguments: ["0xee496a0cc04d06a345982ba6697c90c619020de9e274408c7819f787ff66e1a1::capy::Capy"],
});

tx.transferObjects([rest, coin], tx.pure(currentAddress));
tx.setSender(currentAddress);

const dryrun = await signer.dryRunTransactionBlock({ transactionBlock: tx });
const submittedTx = await signer.signAndExecuteTransactionBlock({ transactionBlock: tx });
await provider.waitForTransactionBlock({ digest: submittedTx.digest });

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí