Skip to content

Commit 72c66f5

Browse files
committed
Add .gitignore and initial test for multi-transaction simulation example
1 parent 714b866 commit 72c66f5

File tree

3 files changed

+352
-0
lines changed

3 files changed

+352
-0
lines changed

client_test.go

+340
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import (
77
"time"
88

99
"github.com/aptos-labs/aptos-go-sdk/api"
10+
"github.com/aptos-labs/aptos-go-sdk/bcs"
11+
"github.com/aptos-labs/aptos-go-sdk/crypto"
1012
"github.com/stretchr/testify/assert"
1113
)
1214

1315
const (
1416
singleSignerScript = "a11ceb0b060000000701000402040a030e0c041a04051e20073e30086e2000000001010204010001000308000104030401000105050601000002010203060c0305010b0001080101080102060c03010b0001090002050b00010900000a6170746f735f636f696e04636f696e04436f696e094170746f73436f696e087769746864726177076465706f7369740000000000000000000000000000000000000000000000000000000000000001000001080b000b0138000c030b020b03380102"
17+
multiSignerScript = "a11ceb0b0700000a0601000203020605080d071525083a40107a1f010200030201000104060c060c03050003060c0503083c53454c463e5f30046d61696e0d6170746f735f6163636f756e74087472616e73666572ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000114636f6d70696c6174696f6e5f6d65746164617461090003322e3003322e31000001090b000a030a0211000b010b030b02110002"
1518
fundAmount = 100_000_000
1619
vmStatusSuccess = "Executed successfully"
1720
)
@@ -22,11 +25,19 @@ var TestSigners map[string]CreateSigner
2225

2326
type CreateSingleSignerPayload func(client *Client, sender TransactionSigner, options ...any) (*RawTransaction, error)
2427

28+
type CreateMultiSignerPayload func(client *Client, sender TransactionSigner, options ...any) (*RawTransactionWithData, error)
29+
2530
var TestSingleSignerPayloads map[string]CreateSingleSignerPayload
2631

32+
var TestFeePayerSignerPayloads map[string]CreateMultiSignerPayload
33+
34+
var TestMultiSignerPayloads map[string]CreateMultiSignerPayload
35+
2736
func init() {
2837
initSigners()
2938
initSingleSignerPayloads()
39+
initMultiSignerPayloads()
40+
initFeePayerSignerPayloads()
3041
}
3142

3243
func initSigners() {
@@ -65,6 +76,17 @@ func initSingleSignerPayloads() {
6576
TestSingleSignerPayloads["Script"] = buildSingleSignerScript
6677
}
6778

79+
func initFeePayerSignerPayloads() {
80+
TestFeePayerSignerPayloads = make(map[string]CreateMultiSignerPayload)
81+
TestFeePayerSignerPayloads["Entry Function"] = buildFeePayerSignerEntryFunction
82+
TestFeePayerSignerPayloads["Script"] = buildFeePayerSignerScript
83+
}
84+
85+
func initMultiSignerPayloads() {
86+
TestMultiSignerPayloads = make(map[string]CreateMultiSignerPayload)
87+
TestMultiSignerPayloads["Script"] = buildMultiSignerScript
88+
}
89+
6890
func TestNamedConfig(t *testing.T) {
6991
names := []string{"mainnet", "devnet", "testnet", "localnet"}
7092
for _, name := range names {
@@ -90,6 +112,38 @@ func Test_SingleSignerFlows(t *testing.T) {
90112
}
91113
}
92114

115+
func Test_MultiSignerFlows(t *testing.T) {
116+
for name, signer := range TestSigners {
117+
for payloadName, buildMultiSignerPayload := range TestFeePayerSignerPayloads {
118+
t.Run(name+" "+payloadName+" Feepayer", func(t *testing.T) {
119+
testMultiTransaction(t, signer, &signer, &[]CreateSigner{}, buildMultiSignerPayload)
120+
})
121+
t.Run(name+" "+payloadName+" Multi Signer", func(t *testing.T) {
122+
testMultiTransaction(t, signer, nil, &[]CreateSigner{
123+
func() (TransactionSigner, error) {
124+
signer, err := NewEd25519Account()
125+
return any(signer).(TransactionSigner), err
126+
}}, buildMultiSignerPayload)
127+
})
128+
t.Run(name+" "+payloadName+" simulation", func(t *testing.T) {
129+
// createFeePayerFn := CreateSigner(func() (TransactionSigner, error) {
130+
// signer, err := NewEd25519Account()
131+
// return any(signer).(TransactionSigner), err
132+
// })
133+
// println("createFeePa********yer", createFeePayerFn)
134+
// var createFeePayer *CreateSigner = &createFeePayerFn
135+
additionalSigners := []CreateSigner{
136+
func() (TransactionSigner, error) {
137+
signer, err := NewEd25519Account()
138+
return any(signer).(TransactionSigner), err
139+
},
140+
}
141+
testMultiTransactionSimulation(t, signer, nil, &additionalSigners, buildMultiSignerPayload)
142+
})
143+
}
144+
}
145+
}
146+
93147
func setupIntegrationTest(t *testing.T, createAccount CreateSigner) (*Client, TransactionSigner) {
94148
// All of these run against localnet
95149
if testing.Short() {
@@ -215,6 +269,184 @@ func testTransactionSimulation(t *testing.T, createAccount CreateSigner, buildTr
215269
assert.Greater(t, simulatedTxn[0].MaxGasAmount, uint64(0))
216270
}
217271

272+
func testMultiTransaction(t *testing.T, createAccount CreateSigner, feePayer *CreateSigner, additional *[]CreateSigner, buildTransaction CreateMultiSignerPayload) {
273+
274+
client, account := setupIntegrationTest(t, createAccount)
275+
276+
// Build transaction
277+
var buildTransactionOptions []any
278+
279+
var feePayerAddress AccountAddress
280+
var feePayerSigner TransactionSigner
281+
282+
// Add feePayer to options if provided
283+
if feePayer != nil {
284+
_, feePayerSigner = setupIntegrationTest(t, *feePayer)
285+
feePayerAddress = feePayerSigner.AccountAddress()
286+
buildTransactionOptions = append(buildTransactionOptions, FeePayer(&feePayerAddress))
287+
}
288+
289+
var additionalAddress []AccountAddress
290+
var additionalSigners []TransactionSigner
291+
// Add additionalSigners to options if provided
292+
if additional != nil && len(*additional) > 0 {
293+
// Use the spread operator to properly expand the slice of signers
294+
for _, signerCreator := range *additional {
295+
_, signerAccount := setupIntegrationTest(t, signerCreator)
296+
additionalAddress = append(additionalAddress, signerAccount.AccountAddress())
297+
additionalSigners = append(additionalSigners, signerAccount)
298+
}
299+
buildTransactionOptions = append(buildTransactionOptions, AdditionalSigners(additionalAddress))
300+
}
301+
302+
// Build transaction with dynamically constructed options
303+
rawTxn, err := buildTransaction(client, account, buildTransactionOptions...)
304+
assert.NoError(t, err)
305+
306+
senderAuth, err := rawTxn.Sign(account)
307+
assert.NoError(t, err)
308+
var FeePayerAuthenticator *crypto.AccountAuthenticator
309+
if feePayer != nil {
310+
feePayerAuth, err := rawTxn.Sign(feePayerSigner)
311+
assert.NoError(t, err)
312+
FeePayerAuthenticator = feePayerAuth
313+
}
314+
315+
// Sign with additional signers and collect authenticators
316+
var additionalAuths []crypto.AccountAuthenticator
317+
for _, additionalSigner := range additionalSigners {
318+
additionalAuth, err := rawTxn.Sign(additionalSigner)
319+
assert.NoError(t, err)
320+
additionalAuths = append(additionalAuths, *additionalAuth)
321+
}
322+
var signedTxn *SignedTransaction
323+
324+
if feePayer != nil {
325+
signed_txn, ok := rawTxn.ToFeePayerSignedTransaction(senderAuth, FeePayerAuthenticator, additionalAuths)
326+
if !ok {
327+
t.Fatal("Failed to build a signed multiagent transaction")
328+
}
329+
signedTxn = signed_txn
330+
} else {
331+
signed_txn, ok := rawTxn.ToMultiAgentSignedTransaction(senderAuth, additionalAuths)
332+
if !ok {
333+
t.Fatal("Failed to build a signed multiagent transaction")
334+
}
335+
signedTxn = signed_txn
336+
}
337+
338+
// Send transaction
339+
result, err := client.SubmitTransaction(signedTxn)
340+
assert.NoError(t, err)
341+
342+
hash := result.Hash
343+
344+
// Wait for the transaction
345+
_, err = client.WaitForTransaction(hash)
346+
assert.NoError(t, err)
347+
348+
// Read transaction by hash
349+
txn, err := client.TransactionByHash(hash)
350+
assert.NoError(t, err)
351+
352+
// Read transaction by version
353+
userTxn, _ := txn.Inner.(*api.UserTransaction)
354+
version := userTxn.Version
355+
356+
// Load the transaction again
357+
txnByVersion, err := client.TransactionByVersion(version)
358+
assert.NoError(t, err)
359+
360+
// Assert that both are the same
361+
expectedTxn, err := txn.UserTransaction()
362+
assert.NoError(t, err)
363+
actualTxn, err := txnByVersion.UserTransaction()
364+
assert.NoError(t, err)
365+
assert.Equal(t, expectedTxn, actualTxn)
366+
}
367+
368+
func testMultiTransactionSimulation(t *testing.T, createAccount CreateSigner, feePayer *CreateSigner, additional *[]CreateSigner, buildTransaction CreateMultiSignerPayload) {
369+
client, account := setupIntegrationTest(t, createAccount)
370+
371+
// Build transaction
372+
var buildTransactionOptions []any
373+
374+
var feePayerAddress AccountAddress
375+
var feePayerSigner TransactionSigner
376+
377+
// Add feePayer to options if provided
378+
if feePayer != nil {
379+
_, feePayerSigner = setupIntegrationTest(t, *feePayer)
380+
feePayerAddress = feePayerSigner.AccountAddress()
381+
buildTransactionOptions = append(buildTransactionOptions, FeePayer(&feePayerAddress))
382+
}
383+
384+
// Build transaction with dynamically constructed options
385+
rawTxn, err := buildTransaction(client, account, buildTransactionOptions...)
386+
assert.NoError(t, err)
387+
388+
additionalSignersFn := func(additional *[]CreateSigner) []crypto.AccountAuthenticator {
389+
var additionalSigners []crypto.AccountAuthenticator
390+
// Add additionalSigners to options if provided
391+
if additional != nil && len(*additional) > 0 {
392+
// Use the spread operator to properly expand the slice of signers
393+
for _, signerCreator := range *additional {
394+
_, _ = setupIntegrationTest(t, signerCreator)
395+
additionalSigners = append(additionalSigners, crypto.AccountAuthenticator{
396+
Variant: crypto.AccountAuthenticatorNoAccount,
397+
Auth: &crypto.AccountAuthenticatorNoAccountAuthenticator{},
398+
})
399+
}
400+
}
401+
return additionalSigners
402+
}
403+
404+
simulatedTxn, err := client.SimulateMultiTransaction(rawTxn, account, additionalSignersFn(additional))
405+
switch account.(type) {
406+
case *MultiKeyTestSigner:
407+
// multikey simulation currently not supported
408+
assert.Error(t, err)
409+
assert.ErrorContains(t, err, "currently unsupported sender derivation scheme")
410+
return // skip rest of the tests
411+
default:
412+
assert.NoError(t, err)
413+
assert.Equal(t, true, simulatedTxn[0].Success)
414+
assert.Equal(t, vmStatusSuccess, simulatedTxn[0].VmStatus)
415+
assert.Greater(t, simulatedTxn[0].GasUsed, uint64(0))
416+
}
417+
418+
// simulate transaction (estimate gas unit price)
419+
rawTxnZeroGasUnitPrice, err := buildTransaction(client, account, GasUnitPrice(0))
420+
assert.NoError(t, err)
421+
simulatedTxn, err = client.SimulateMultiTransaction(rawTxnZeroGasUnitPrice, account, []crypto.AccountAuthenticator{}, EstimateGasUnitPrice(true))
422+
assert.NoError(t, err)
423+
assert.Equal(t, true, simulatedTxn[0].Success)
424+
assert.Equal(t, vmStatusSuccess, simulatedTxn[0].VmStatus)
425+
estimatedGasUnitPrice := simulatedTxn[0].GasUnitPrice
426+
assert.Greater(t, estimatedGasUnitPrice, uint64(0))
427+
428+
// simulate transaction (estimate max gas amount)
429+
rawTxnZeroMaxGasAmount, err := buildTransaction(client, account, MaxGasAmount(0))
430+
assert.NoError(t, err)
431+
simulatedTxn, err = client.SimulateMultiTransaction(rawTxnZeroMaxGasAmount, account, []crypto.AccountAuthenticator{}, EstimateMaxGasAmount(true))
432+
assert.NoError(t, err)
433+
assert.Equal(t, true, simulatedTxn[0].Success)
434+
assert.Equal(t, vmStatusSuccess, simulatedTxn[0].VmStatus)
435+
assert.Greater(t, simulatedTxn[0].MaxGasAmount, uint64(0))
436+
437+
// simulate transaction (estimate prioritized gas unit price and max gas amount)
438+
rawTxnZeroGasConfig, err := buildTransaction(client, account, GasUnitPrice(0), MaxGasAmount(0))
439+
assert.NoError(t, err)
440+
simulatedTxn, err = client.SimulateMultiTransaction(rawTxnZeroGasConfig, account, []crypto.AccountAuthenticator{}, EstimatePrioritizedGasUnitPrice(true), EstimateMaxGasAmount(true))
441+
assert.NoError(t, err)
442+
assert.Equal(t, true, simulatedTxn[0].Success)
443+
assert.Equal(t, vmStatusSuccess, simulatedTxn[0].VmStatus)
444+
estimatedGasUnitPrice = simulatedTxn[0].GasUnitPrice
445+
assert.Greater(t, estimatedGasUnitPrice, uint64(0))
446+
assert.Greater(t, simulatedTxn[0].MaxGasAmount, uint64(0))
447+
448+
}
449+
218450
func TestAPTTransferTransaction(t *testing.T) {
219451
sender, err := NewEd25519Account()
220452
assert.NoError(t, err)
@@ -685,6 +917,38 @@ func buildSingleSignerEntryFunction(client *Client, sender TransactionSigner, op
685917
return APTTransferTransaction(client, sender, AccountOne, 100, options...)
686918
}
687919

920+
func buildFeePayerSignerEntryFunction(client *Client, sender TransactionSigner, options ...any) (*RawTransactionWithData, error) {
921+
amount := uint64(100)
922+
amountBytes, err := bcs.SerializeU64(amount)
923+
if err != nil {
924+
return nil, err
925+
}
926+
dest := AccountOne
927+
928+
rawTxn, err := client.BuildTransactionMultiAgent(
929+
sender.AccountAddress(),
930+
TransactionPayload{
931+
Payload: &EntryFunction{
932+
Module: ModuleId{
933+
Address: AccountOne,
934+
Name: "aptos_account",
935+
},
936+
Function: "transfer",
937+
ArgTypes: []TypeTag{},
938+
Args: [][]byte{
939+
dest[:],
940+
amountBytes,
941+
},
942+
}},
943+
options...,
944+
)
945+
if err != nil {
946+
return nil, err
947+
}
948+
949+
return rawTxn, nil
950+
}
951+
688952
func buildSingleSignerScript(client *Client, sender TransactionSigner, options ...any) (*RawTransaction, error) {
689953
scriptBytes, err := ParseHex(singleSignerScript)
690954
if err != nil {
@@ -714,3 +978,79 @@ func buildSingleSignerScript(client *Client, sender TransactionSigner, options .
714978

715979
return rawTxn, nil
716980
}
981+
982+
func buildFeePayerSignerScript(client *Client, sender TransactionSigner, options ...any) (*RawTransactionWithData, error) {
983+
scriptBytes, err := ParseHex(singleSignerScript)
984+
if err != nil {
985+
return nil, err
986+
}
987+
988+
amount := uint64(1)
989+
dest := AccountOne
990+
991+
rawTxn, err := client.BuildTransactionMultiAgent(
992+
sender.AccountAddress(),
993+
TransactionPayload{
994+
Payload: &Script{
995+
Code: scriptBytes,
996+
ArgTypes: []TypeTag{},
997+
Args: []ScriptArgument{{
998+
Variant: ScriptArgumentU64,
999+
Value: amount,
1000+
}, {
1001+
Variant: ScriptArgumentAddress,
1002+
Value: dest,
1003+
}},
1004+
},
1005+
},
1006+
options...,
1007+
)
1008+
if err != nil {
1009+
panic("Failed to build multiagent raw transaction:" + err.Error())
1010+
}
1011+
1012+
if err != nil {
1013+
return nil, err
1014+
}
1015+
1016+
return rawTxn, nil
1017+
}
1018+
1019+
func buildMultiSignerScript(client *Client, sender TransactionSigner, options ...any) (*RawTransactionWithData, error) {
1020+
scriptBytes, err := ParseHex(multiSignerScript)
1021+
if err != nil {
1022+
return nil, err
1023+
}
1024+
1025+
amount := uint64(1)
1026+
1027+
rawTxn, err := client.BuildTransactionMultiAgent(
1028+
sender.AccountAddress(),
1029+
TransactionPayload{
1030+
Payload: &Script{
1031+
Code: scriptBytes,
1032+
ArgTypes: []TypeTag{},
1033+
Args: []ScriptArgument{
1034+
{
1035+
Variant: ScriptArgumentU64,
1036+
Value: uint64(amount),
1037+
},
1038+
{
1039+
Variant: ScriptArgumentAddress,
1040+
Value: AccountOne,
1041+
},
1042+
},
1043+
},
1044+
},
1045+
options...,
1046+
)
1047+
if err != nil {
1048+
panic("Failed to build multiagent raw transaction:" + err.Error())
1049+
}
1050+
1051+
if err != nil {
1052+
return nil, err
1053+
}
1054+
1055+
return rawTxn, nil
1056+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fungible_assets

0 commit comments

Comments
 (0)