Skip to content

Commit 4fe5f52

Browse files
committed
Add Serialized type and serialization methods for handling byte arrays
1 parent affcbec commit 4fe5f52

File tree

5 files changed

+173
-10
lines changed

5 files changed

+173
-10
lines changed

bcs/deserializer.go

+5
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,8 @@ func (des *Deserializer) setError(msg string, args ...any) {
318318
}
319319
des.err = fmt.Errorf(msg, args...)
320320
}
321+
322+
// DeserializeBytes deserializes a byte array prefixed with a length
323+
func (des *Deserializer) Serialized() *Serialized {
324+
return &Serialized{des.ReadBytes()}
325+
}

bcs/serialized.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package bcs
2+
3+
// Serialized represents a serialized transaction argument
4+
type Serialized struct {
5+
Value []byte
6+
}
7+
8+
// NewSerialized creates a new Serialized instance
9+
func NewSerialized(value []byte) *Serialized {
10+
return &Serialized{
11+
Value: value,
12+
}
13+
}
14+
15+
// Serialize serializes the Serialized instance
16+
func (s *Serialized) Serialized(serializer *Serializer) {
17+
serializer.WriteBytes(s.Value)
18+
}
19+
20+
// SerializeForEntryFunction serializes the Serialized instance for entry function
21+
func (s *Serialized) SerializedForEntryFunction(serializer *Serializer) {
22+
s.Serialized(serializer)
23+
}
24+
25+
// SerializeForScriptFunction serializes the Serialized instance for script function
26+
func (s *Serialized) SerializedForScriptFunction(serializer *Serializer) {
27+
serializer.Uleb128(uint32(9))
28+
s.Serialized(serializer)
29+
}

bcs/serializer.go

+13
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,16 @@ func SerializeOption[T any](ser *Serializer, input *T, serialize func(ser *Seria
352352
SerializeSequenceWithFunction([]T{*input}, ser, serialize)
353353
}
354354
}
355+
356+
// Serialized wraps already serialized bytes with BCS serialization
357+
// It prepends the byte array with its length (encoded as Uleb128) and writes the bytes
358+
// Primarily used for handling nested serialization structures where bytes are already in BCS format
359+
func (ser *Serializer) Serialized(s Serialized) {
360+
ser.WriteBytes(s.Value)
361+
}
362+
363+
func SerializeSerialized(input Serialized) ([]byte, error) {
364+
return SerializeSingle(func(ser *Serializer) {
365+
ser.Serialized(input)
366+
})
367+
}

examples/script_transaction/main.go

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// fungible_asset is an example of how to create and transfer fungible assets
2+
package main
3+
4+
import (
5+
"fmt"
6+
7+
"github.com/aptos-labs/aptos-go-sdk"
8+
"github.com/aptos-labs/aptos-go-sdk/bcs"
9+
"github.com/aptos-labs/aptos-go-sdk/crypto"
10+
)
11+
const testEd25519PrivateKey = "ed25519-priv-0xc5338cd251c22daa8c9c9cc94f498cc8a5c7e1d2e75287a5dda91096fe64efa5"
12+
13+
const TransferScriptPayload = "0x008601a11ceb0b0700000a06010002030206050806070e1708252010451900000001010000010003060c05030d6170746f735f6163636f756e74087472616e736665720000000000000000000000000000000000000000000000000000000000000001166170746f733a3a7363726970745f636f6d706f7365720100000100050a000b010b0211000200020920000000000000000000000000000000000000000000000000000000000000000209086400000000000000"
14+
const TransferAmount = uint64(1_000)
15+
const FundAmount = uint64(100_000_000)
16+
17+
func example(networkConfig aptos.NetworkConfig) {
18+
// Create a client for Aptos
19+
client, err := aptos.NewClient(networkConfig)
20+
if err != nil {
21+
panic("Failed to create client:" + err.Error())
22+
}
23+
24+
// Create a sender locally
25+
key := crypto.Ed25519PrivateKey{}
26+
err = key.FromHex(testEd25519PrivateKey)
27+
if err != nil {
28+
panic("Failed to decode Ed25519 private key:" + err.Error())
29+
}
30+
sender, err := aptos.NewAccountFromSigner(&key)
31+
if err != nil {
32+
panic("Failed to create sender:" + err.Error())
33+
}
34+
35+
// Fund the sender with the faucet to create it on-chain
36+
println("SENDER: ", sender.Address.String())
37+
err = client.Fund(sender.Address, FundAmount)
38+
if err != nil {
39+
panic("Failed to fund sender:" + err.Error())
40+
}
41+
receiver := &aptos.AccountAddress{}
42+
43+
// Now run a script version
44+
fmt.Printf("\n== Now running script version ==\n")
45+
runScript(client, sender, receiver)
46+
47+
if err != nil {
48+
panic("Failed to get store balance:" + err.Error())
49+
}
50+
// fmt.Printf("After Script: Receiver Before transfer: %d, after transfer: %d\n", receiverAfterBalance, receiverAfterAfterBalance)
51+
}
52+
53+
func runScript(client *aptos.Client, alice *aptos.Account, bob *aptos.AccountAddress) {
54+
scriptBytes, err := aptos.ParseHex(TransferScriptPayload)
55+
if err != nil {
56+
panic("Failed to parse script:" + err.Error())
57+
}
58+
59+
var payload aptos.TransactionPayload
60+
payload.UnmarshalBCS(bcs.NewDeserializer(scriptBytes))
61+
62+
63+
// 1. Build transaction
64+
rawTxn, err := client.BuildTransaction(alice.AccountAddress(), payload);
65+
66+
if err != nil {
67+
panic("Failed to build transaction:" + err.Error())
68+
}
69+
70+
// 2. Simulate transaction (optional)
71+
// This is useful for understanding how much the transaction will cost
72+
// and to ensure that the transaction is valid before sending it to the network
73+
// This is optional, but recommended
74+
simulationResult, err := client.SimulateTransaction(rawTxn, alice)
75+
if err != nil {
76+
panic("Failed to simulate transaction:" + err.Error())
77+
}
78+
fmt.Printf("\n=== Simulation ===\n")
79+
fmt.Printf("Gas unit price: %d\n", simulationResult[0].GasUnitPrice)
80+
fmt.Printf("Gas used: %d\n", simulationResult[0].GasUsed)
81+
fmt.Printf("Total gas fee: %d\n", simulationResult[0].GasUsed*simulationResult[0].GasUnitPrice)
82+
fmt.Printf("Status: %s\n", simulationResult[0].VmStatus)
83+
84+
// 3. Sign transaction
85+
signedTxn, err := rawTxn.SignedTransaction(alice)
86+
if err != nil {
87+
panic("Failed to sign transaction:" + err.Error())
88+
}
89+
90+
// 4. Submit transaction
91+
submitResult, err := client.SubmitTransaction(signedTxn)
92+
if err != nil {
93+
panic("Failed to submit transaction:" + err.Error())
94+
}
95+
txnHash := submitResult.Hash
96+
97+
// 5. Wait for the transaction to complete
98+
_, err = client.WaitForTransaction(txnHash)
99+
if err != nil {
100+
panic("Failed to wait for transaction:" + err.Error())
101+
}
102+
}
103+
104+
// main This example shows how to send a transaction with a script
105+
func main() {
106+
example(aptos.DevnetConfig)
107+
}

script.go

+19-10
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package aptos
22

33
import (
44
"fmt"
5-
"github.com/aptos-labs/aptos-go-sdk/bcs"
65
"math/big"
6+
"github.com/aptos-labs/aptos-go-sdk/bcs"
77
)
88

99
//region Script
@@ -48,15 +48,16 @@ func (s *Script) UnmarshalBCS(des *bcs.Deserializer) {
4848
type ScriptArgumentVariant uint32
4949

5050
const (
51-
ScriptArgumentU8 ScriptArgumentVariant = 0 // u8 type argument
52-
ScriptArgumentU64 ScriptArgumentVariant = 1 // u64 type argument
53-
ScriptArgumentU128 ScriptArgumentVariant = 2 // u128 type argument
54-
ScriptArgumentAddress ScriptArgumentVariant = 3 // address type argument
55-
ScriptArgumentU8Vector ScriptArgumentVariant = 4 // vector<u8> type argument
56-
ScriptArgumentBool ScriptArgumentVariant = 5 // bool type argument
57-
ScriptArgumentU16 ScriptArgumentVariant = 6 // u16 type argument
58-
ScriptArgumentU32 ScriptArgumentVariant = 7 // u32 type argument
59-
ScriptArgumentU256 ScriptArgumentVariant = 8 // u256 type argument
51+
ScriptArgumentU8 ScriptArgumentVariant = 0 // u8 type argument
52+
ScriptArgumentU64 ScriptArgumentVariant = 1 // u64 type argument
53+
ScriptArgumentU128 ScriptArgumentVariant = 2 // u128 type argument
54+
ScriptArgumentAddress ScriptArgumentVariant = 3 // address type argument
55+
ScriptArgumentU8Vector ScriptArgumentVariant = 4 // vector<u8> type argument
56+
ScriptArgumentBool ScriptArgumentVariant = 5 // bool type argument
57+
ScriptArgumentU16 ScriptArgumentVariant = 6 // u16 type argument
58+
ScriptArgumentU32 ScriptArgumentVariant = 7 // u32 type argument
59+
ScriptArgumentU256 ScriptArgumentVariant = 8 // u256 type argument
60+
ScriptArgumentSerialized ScriptArgumentVariant = 9 // Serialized type argument
6061
)
6162

6263
// ScriptArgument a Move script argument, which encodes its type with it
@@ -125,6 +126,12 @@ func (sa *ScriptArgument) MarshalBCS(ser *bcs.Serializer) {
125126
ser.SetError(fmt.Errorf("invalid input type (%T) for ScriptArgumentBool, must be bool", sa.Value))
126127
}
127128
ser.Bool(value)
129+
case ScriptArgumentSerialized:
130+
value, ok := (sa.Value).(*bcs.Serialized)
131+
if !ok {
132+
ser.SetError(fmt.Errorf("invalid input type (%T) for ScriptArgumentSerialized, must be *bcs.Serialized", sa.Value))
133+
}
134+
ser.Serialized(*value)
128135
}
129136
}
130137

@@ -151,6 +158,8 @@ func (sa *ScriptArgument) UnmarshalBCS(des *bcs.Deserializer) {
151158
sa.Value = des.ReadBytes()
152159
case ScriptArgumentBool:
153160
sa.Value = des.Bool()
161+
case ScriptArgumentSerialized:
162+
sa.Value = des.Serialized()
154163
}
155164
}
156165

0 commit comments

Comments
 (0)