Add pure and object func on transaction

This commit is contained in:
Chenyang
2025-04-19 00:29:54 +08:00
parent ec67a3e0ae
commit 2d836eccc0
4 changed files with 208 additions and 18 deletions

View File

@@ -1,10 +1,14 @@
package transaction
import (
"bytes"
"errors"
"math"
"github.com/block-vision/sui-go-sdk/models"
"github.com/block-vision/sui-go-sdk/models/sui_types"
"github.com/block-vision/sui-go-sdk/mystenbcs"
"github.com/block-vision/sui-go-sdk/utils"
)
type Transaction struct {
@@ -70,19 +74,15 @@ func (tx *Transaction) Gas() Argument {
}
func (tx *Transaction) Add(command Command) Argument {
tx.Data.Commands = append(tx.Data.Commands, command)
index := uint16(len(tx.Data.Commands) - 1)
index := tx.Data.AddCommand(command)
return createTransactionResult(index, nil)
}
func (tx *Transaction) SplitCoins(coin Argument, amount []Argument) Argument {
cmd := splitCoins(SplitCoinsValue{
return tx.Add(splitCoins(SplitCoinsValue{
Coin: coin,
Amount: amount,
})
tx.Data.Commands = append(tx.Data.Commands, cmd)
index := uint16(len(tx.Data.Commands) - 1)
return createTransactionResult(index, nil)
}))
}
func (tx *Transaction) MergeCoins(destination Argument, sources []Argument) Argument {
@@ -143,18 +143,99 @@ func (tx *Transaction) makeMoveVec(typeValue *string, elements []Argument) Argum
}))
}
func (tx *Transaction) Object(obj string) Argument {
// TODO
return InputObject{
Value: obj,
func (tx *Transaction) Object(inputObject InputObject) (Argument, error) {
var callArg CallArg
// Get inputObject's ID
var id string
if inputObject.Value == nil {
id = inputObject.ObjectId
if id == "" {
return nil, errors.New("object id is empty")
}
callArg = UnresolvedObject{
ObjectId: utils.NormalizeSuiAddress(id),
}
} else {
objArg := inputObject.Value.Value
switch objArg.(type) {
case ImmOrOwnedObject:
id = objArg.(ImmOrOwnedObject).Value.ObjectId
case SharedObject:
id = objArg.(SharedObject).Value.ObjectId
case Receiving:
id = objArg.(Receiving).Value.ObjectId
default:
return nil, errors.New("object value is not supported")
}
callArg = Object{
Value: objArg,
}
}
// Check obj id if exists in tx
isInserted := false
for _, input := range tx.Data.Inputs {
var inputId string
switch input.(type) {
case Object:
obj := input.(Object).Value
switch obj.(type) {
case ImmOrOwnedObject:
inputId = obj.(ImmOrOwnedObject).Value.ObjectId
case SharedObject:
inputId = obj.(SharedObject).Value.ObjectId
case Receiving:
inputId = obj.(Receiving).Value.ObjectId
default:
return nil, errors.New("object value on tx is not supported")
}
case UnresolvedObject:
inputId = input.(UnresolvedObject).ObjectId
}
if inputId == id {
isInserted = true
break
}
}
if isInserted {
return nil, errors.New("object id already exists in tx")
}
input := tx.Data.AddInput(callArg)
arg := Input{
Input: input.(Input).Input,
Type: input.(Input).Type,
}
return arg, nil
}
func (tx *Transaction) Pure(input string) Argument {
// TODO
return InputPure{
Value: []byte{},
func (tx *Transaction) Pure(inputPure InputPure) (Argument, error) {
value := inputPure.Value
bcsEncodedMsg := bytes.Buffer{}
bcsEncoder := mystenbcs.NewEncoder(&bcsEncodedMsg)
err := bcsEncoder.Encode(value)
if err != nil {
return nil, err
}
bcsBase64 := mystenbcs.ToBase64(bcsEncodedMsg.Bytes())
input := tx.Data.AddInput(Pure{
bcsBase64,
})
arg := Input{
Input: input.(Input).Input,
Type: input.(Input).Type,
}
return arg, nil
}
func createTransactionResult(index uint16, length *uint16) Argument {

View File

@@ -1,6 +1,8 @@
package transaction
import (
"strings"
"github.com/block-vision/sui-go-sdk/models"
"github.com/block-vision/sui-go-sdk/models/sui_types"
)
@@ -14,6 +16,23 @@ type TransactionData struct {
Commands []Command
}
func (td *TransactionData) AddCommand(command Command) (index uint16) {
index = uint16(len(td.Commands))
td.Commands = append(td.Commands, command)
return index
}
func (td *TransactionData) AddInput(input CallArg) Argument {
index := len(td.Inputs)
td.Inputs = append(td.Inputs, input)
return Input{
Input: uint16(index),
Type: strings.ToLower(input.callArgKind()),
}
}
// GasData https://github.com/MystenLabs/sui/blob/fb27c6c7166f5e4279d5fd1b2ebc5580ca0e81b2/crates/sui-types/src/transaction.rs#L1600
type GasData struct {
Payment []sui_types.SuiObjectRef
@@ -36,12 +55,15 @@ type ProgrammableTransaction struct {
// CallArg https://github.com/MystenLabs/sui/blob/fb27c6c7166f5e4279d5fd1b2ebc5580ca0e81b2/crates/sui-types/src/transaction.rs#L80
// - Pure
// - Object
// - UnresolvedPure
// - UnresolvedObject
type CallArg interface {
callArgKind() string
}
type Pure struct {
Value []byte
// BCSBates's Base64
Bytes string
}
func (p Pure) callArgKind() string {
@@ -56,6 +78,22 @@ func (o Object) callArgKind() string {
return "Object"
}
type UnresolvedPure struct {
Value string
}
func (u UnresolvedPure) callArgKind() string {
return "UnresolvedPure"
}
type UnresolvedObject struct {
ObjectId string
}
func (u UnresolvedObject) callArgKind() string {
return "UnresolvedObject"
}
// ObjectArg
// - ImmOrOwnedObject
// - SharedObject
@@ -199,6 +237,7 @@ type UpgradeValue struct {
// Argument https://github.com/MystenLabs/sui/blob/fb27c6c7166f5e4279d5fd1b2ebc5580ca0e81b2/crates/sui-types/src/transaction.rs#L745
// - GasCoin
// - Input
// - InputPure
// - InputObject
// - Result
@@ -215,8 +254,18 @@ func (g GasCoin) argumentKind() string {
return "GasCoin"
}
type Input struct {
// Index
Input uint16
Type string
}
func (i Input) argumentKind() string {
return "Input"
}
type InputPure struct {
Value []byte
Value string
}
func (i InputPure) argumentKind() string {
@@ -224,7 +273,8 @@ func (i InputPure) argumentKind() string {
}
type InputObject struct {
Value string
ObjectId string
Value *Object
}
func (i InputObject) argumentKind() string {

View File

@@ -6,11 +6,22 @@ import (
"encoding/json"
"fmt"
"reflect"
"strings"
"github.com/mr-tron/base58"
"golang.org/x/crypto/blake2b"
)
func NormalizeSuiAddress(input string) string {
addr := strings.ToLower(input)
if strings.HasPrefix(addr, "0x") {
addr = addr[2:]
}
addr = strings.Repeat("0", 64-len(addr)) + addr
return "0x" + addr
}
func PrettyPrint(v interface{}) {
b, err := json.Marshal(v)
if err != nil {

48
utils/utils_test.go Normal file
View File

@@ -0,0 +1,48 @@
package utils
import (
"testing"
)
func TestNormalizeSuiAddress(t *testing.T) {
tests := []struct {
input string
expected string
description string
}{
{
input: "abc",
expected: "0x0000000000000000000000000000000000000000000000000000000000000abc",
description: "lowercase without 0x",
},
{
input: "0xabc",
expected: "0x0000000000000000000000000000000000000000000000000000000000000abc",
description: "with 0x, no forceAdd0x",
},
{
input: "0xABC",
expected: "0x0000000000000000000000000000000000000000000000000000000000000abc",
description: "uppercase input, normalize to lowercase",
},
{
input: "abc",
expected: "0x0000000000000000000000000000000000000000000000000000000000000abc",
description: "forceAdd0x with plain input",
},
{
input: "0xabc",
expected: "0x0000000000000000000000000000000000000000000000000000000000000abc",
description: "forceAdd0x true keeps 0xabc and still pads correctly",
},
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
result := NormalizeSuiAddress(test.input)
if result != test.expected {
t.Errorf("expected %s, got %s", test.expected, result)
}
})
}
}