Go

HTTP

Code Examplearrow-up-right

package main

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"time"

	"github.com/BlockRazorinc/sui_go_example/blockrzsdk"

	"github.com/block-vision/sui-go-sdk/models"
	"github.com/block-vision/sui-go-sdk/signer"
	"github.com/block-vision/sui-go-sdk/sui"
	"github.com/block-vision/sui-go-sdk/transaction"
)

const (
	// Official Sui fullnode RPC (mainnet)
	OfficialRpcUrl = "https://fullnode.mainnet.sui.io"
	// Gas object id
	GasObjectId = "<YOUR_GAS_OBJECT_ID>"
	// Receiver address
	ReceiverAddr = "<YOUR_RECEIVER_ADDRESS>"
	// Transfer amount in MIST
	SendAmountMist = uint64(100000)
	// Default gas price
	GasPrice = uint64(5000)
	// BlockRz RPC endpoint
	BlockRzRpcUrl = "<YOUR_BLOCKRZ_RPC_URL>"
	// Auth token header for your RPC
	AuthToken = "<YOUR_AUTH_TOKEN>"
	// Timeouts
	BuildTimeout = 5 * time.Second
	HttpTimeout  = 15 * time.Second
)

// fetchLatestObjectRef fetches the latest (version, digest) for an objectId
func fetchLatestObjectRef(ctx context.Context, cli *sui.Client, objectId string) (*transaction.SuiObjectRef, error) {
	obj, err := cli.SuiGetObject(ctx, models.SuiGetObjectRequest{ObjectId: objectId})
	if err != nil {
		return nil, fmt.Errorf("SuiGetObject failed: %w", err)
	}
	if obj.Data == nil {
		return nil, fmt.Errorf("object not found or deleted: %s", objectId)
	}
	if obj.Data.Version == "" || obj.Data.Digest == "" {
		return nil, fmt.Errorf("invalid object data: id=%s version=%s digest=%s", objectId, obj.Data.Version, obj.Data.Digest)
	}

	ref, err := transaction.NewSuiObjectRef(
		models.SuiAddress(objectId),
		obj.Data.Version,
		models.ObjectDigest(obj.Data.Digest),
	)
	if err != nil {
		return nil, fmt.Errorf("NewSuiObjectRef failed: %w", err)
	}
	return ref, nil
}

// makeHttpRequest builds the JSON-RPC request for `sui_executeTransactionBlock`.
func makeHttpRequest(tx *transaction.Transaction) (*http.Request, []byte, error) {
	reqModel, err := tx.ToSuiExecuteTransactionBlockRequest(
		context.Background(),
		models.SuiTransactionBlockOptions{ShowEffects: true},
		"WaitForLocalExecution",
	)
	if err != nil {
		return nil, nil, fmt.Errorf("ToSuiExecuteTransactionBlockRequest failed: %w", err)
	}

	bodyMap := map[string]any{
		"jsonrpc": "2.0",
		"id":      1,
		"method":  "sui_executeTransactionBlock",
		"params": []any{
			reqModel.TxBytes,
			reqModel.Signature,
			reqModel.Options,
			reqModel.RequestType,
		},
	}

	jsonBytes, err := json.Marshal(bodyMap)
	if err != nil {
		return nil, nil, fmt.Errorf("json.Marshal failed: %w", err)
	}
	req, err := http.NewRequest("POST", BlockRzRpcUrl, bytes.NewBuffer(jsonBytes))
	if err != nil {
		return nil, nil, fmt.Errorf("NewRequest failed: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("auth_token", AuthToken)
	rawTxBytes, err := base64.StdEncoding.DecodeString(reqModel.TxBytes)
	if err != nil {
		return nil, nil, fmt.Errorf("base64 decode TxBytes failed: %w", err)
	}
	return req, rawTxBytes, nil
}

// buildBaseTx creates a basic transfer tx: split from gas coin then transfer to receiver.
func buildBaseTx(cli *sui.Client, s *signer.Signer, gasRef *transaction.SuiObjectRef) *transaction.Transaction {
	tx := transaction.NewTransaction()
	tx.SetSuiClient(cli).
		SetSigner(s).
		SetSender(models.SuiAddress(s.Address)).
		SetGasPayment([]transaction.SuiObjectRef{*gasRef}).
		SetGasOwner(models.SuiAddress(s.Address))

	split := tx.SplitCoins(tx.Gas(), []transaction.Argument{tx.Pure(SendAmountMist)})
	tx.TransferObjects([]transaction.Argument{split}, tx.Pure(ReceiverAddr))
	return tx
}

func main() {
	ctx := context.Background()
	// Build signer from secret key
	secretKey := "<YOUR_SECRET_KEY>"
	sender, err := signer.NewSignerWithSecretKey(secretKey)
	if err != nil {
		log.Fatalf("[INIT] NewSignerWithSecretKey failed: %v", err)
	}
	log.Printf("[INIT] sender=%s receiver=%s amountMist=%d", sender.Address, ReceiverAddr, SendAmountMist)

	// Create Sui client (official fullnode)
	cli := sui.NewSuiClient(OfficialRpcUrl).(*sui.Client)

	// Fetch latest gas object ref
	gasRef, err := fetchLatestObjectRef(ctx, cli, GasObjectId)
	if err != nil {
		log.Fatalf("[GAS] fetchLatestObjectRef failed: %v", err)
	}
	log.Printf("[GAS] gasObject=%s version=%d digest=%s", GasObjectId, gasRef.Version, gasRef.Digest)

	// Build base tx
	tx := buildBaseTx(cli, sender, gasRef)

	// ============================================
	//  Estimate fee & tip using dryRun
	// ============================================
	tx.SetGasPrice(GasPrice)
	buildCtx, cancel := context.WithTimeout(context.Background(), BuildTimeout)
	defer cancel()

	// Build TxBytes for estimation
	reqModel, err := tx.ToSuiExecuteTransactionBlockRequest(
		buildCtx,
		models.SuiTransactionBlockOptions{},
		"WaitForLocalExecution",
	)
	if err != nil {
		log.Fatalf("[PREP] build tx bytes failed: %v", err)
	}

	// Caculate Fee
	est, err := blockrzsdk.CalculateFeeFromTxB64(buildCtx, reqModel.TxBytes)
	if err != nil {
		log.Fatalf("[PREP] CalculateFeeFromTxB64 failed: %v", err)
	}
	tx.SetGasBudget(est.GasBudget)

	// Add tip into transaction
	// The required tip must exceed 1,000,000 mist
	// and must exceed 5% of the gas budget
	const minTip uint64 = 1000000
	tipFee := est.TipAmount
	if tipFee < minTip {
		tipFee = minTip
	}
	if err := blockrzsdk.AddTip(tx, tipFee); err != nil {
		log.Fatalf("[PREP] AddTip failed: %v", err)
	}
	log.Printf("[PREP] gasPrice=%d gasBudget=%d tip=%d txBytesLen=%d",
		GasPrice, est.GasBudget, tipFee, len(reqModel.TxBytes))

	// ============================================
	//  Build HTTP request and Send to BlockRz
	// ============================================
	req, _, err := makeHttpRequest(tx)
	if err != nil {
		log.Fatalf("[HTTP] makeHttpRequest failed: %v", err)
	}

	// Send HTTP request and read response body
	client := &http.Client{Timeout: HttpTimeout}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("[HTTP] request failed: %v", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("[HTTP] read response body failed: %v", err)
	}
	log.Printf("[HTTP][RESP] body=%s", body)
}

gRPC

Code Examplearrow-up-right

Last updated