Skip to content

Commit 26a4956

Browse files
authored
Merge pull request #20 from icon-project/feature/transaction-creation-and-broadcasting
Feature/transaction creation and broadcasting
2 parents b7402e2 + 2fd02fa commit 26a4956

22 files changed

+2406
-5
lines changed

README.md

+47
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,53 @@ func main() {
6161
}
6262
```
6363

64+
### Creating and Broadcasting a Token Transfer Transaction
65+
```go
66+
import (
67+
"github.com/balancednetwork/stacks-go-sdk/stacks"
68+
"github.com/balancednetwork/stacks-go-sdk/clarity"
69+
)
70+
71+
func main() {
72+
network := stacks.NewStacksMainnet()
73+
74+
contractAddress := "SP466FNC0P7JWTNM2R9T199QRZN1MYEDTAR0KP27"
75+
contractName := "contract-name"
76+
functionName := "function-name"
77+
senderAddress := "SP1P72Z3704VMT3DMHPP2CB8TGQWGDBHD3RPR9GZS"
78+
senderKey := []byte{...} // sender's private key
79+
80+
// Prepare function arguments
81+
arg1, _ := clarity.NewInt(123)
82+
arg2 := clarity.NewStringType("example")
83+
functionArgs := []clarity.ClarityValue{arg1, arg2}
84+
85+
// Create and sign the transaction
86+
tx, err := stacks.MakeContractCall(
87+
contractAddress,
88+
contractName,
89+
functionName,
90+
functionArgs,
91+
*network,
92+
senderAddress,
93+
senderKey,
94+
nil, // Let the function estimate the fee
95+
nil, // Let the function fetch the nonce
96+
)
97+
if err != nil {
98+
// Handle error
99+
}
100+
101+
// Broadcast the transaction
102+
txID, err := stacks.BroadcastTransaction(tx, network)
103+
if err != nil {
104+
// Handle error
105+
}
106+
107+
println("Contract call transaction broadcast successfully. Transaction ID:", txID)
108+
}
109+
```
110+
64111
### Working with Clarity Values
65112
```golang
66113
import (

go.mod

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module github.com/icon-project/stacks-go-sdk
2+
3+
go 1.22.5
4+
5+
require (
6+
github.com/btcsuite/btcd/btcec/v2 v2.3.4
7+
github.com/go-resty/resty/v2 v2.14.0
8+
github.com/stretchr/testify v1.9.0
9+
github.com/tyler-smith/go-bip32 v1.0.0
10+
github.com/tyler-smith/go-bip39 v1.1.0
11+
golang.org/x/crypto v0.26.0
12+
)
13+
14+
require (
15+
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect
16+
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect
17+
github.com/davecgh/go-spew v1.1.1 // indirect
18+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
19+
github.com/pmezard/go-difflib v1.0.0 // indirect
20+
golang.org/x/net v0.27.0 // indirect
21+
gopkg.in/yaml.v3 v3.0.1 // indirect
22+
)

go.sum

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc=
2+
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
3+
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc=
4+
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
5+
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
6+
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
7+
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
8+
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
9+
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw=
10+
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
11+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
13+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14+
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
15+
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
16+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
17+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
18+
github.com/go-resty/resty/v2 v2.14.0 h1:/rhkzsAqGQkozwfKS5aFAbb6TyKd3zyFRWcdRXLPCAU=
19+
github.com/go-resty/resty/v2 v2.14.0/go.mod h1:IW6mekUOsElt9C7oWr0XRt9BNSD6D5rr9mhk6NjmNHg=
20+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
21+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
22+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
23+
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
24+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
25+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
26+
github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE=
27+
github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
28+
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
29+
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
30+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
31+
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
32+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
33+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
34+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
35+
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
36+
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
37+
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
38+
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
39+
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
40+
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
41+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
42+
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
43+
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
44+
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
45+
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
46+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
47+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
48+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
49+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
50+
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
51+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
52+
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
53+
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
54+
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
55+
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
56+
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
57+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
58+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
59+
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
60+
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
61+
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
62+
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
63+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
64+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
65+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
66+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
67+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
68+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
69+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
70+
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
71+
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
72+
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
73+
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
74+
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
75+
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
76+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
77+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
78+
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
79+
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
80+
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
81+
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
82+
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
83+
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
84+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
85+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
86+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
87+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
88+
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
89+
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
90+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
91+
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
92+
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
93+
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
94+
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
95+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
96+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
97+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
98+
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
99+
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
100+
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
101+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
102+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
103+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
104+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
105+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
106+
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
107+
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=

internal/utils/utils.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package utils
2+
3+
import (
4+
"errors"
5+
6+
"github.com/icon-project/stacks-go-sdk/pkg/stacks"
7+
)
8+
9+
func SerializeString(s string, maxLength int) ([]byte, error) {
10+
if len(s) > maxLength {
11+
return nil, errors.New("string exceeds maximum length")
12+
}
13+
buf := make([]byte, 1+len(s))
14+
buf[0] = byte(len(s))
15+
copy(buf[1:], s)
16+
return buf, nil
17+
}
18+
19+
func DeserializeString(data []byte, maxLength int) (string, int, error) {
20+
if len(data) < 1 {
21+
return "", 0, errors.New("insufficient data for string length")
22+
}
23+
length := int(data[0])
24+
if length > maxLength || len(data) < 1+length {
25+
return "", 0, errors.New("invalid string length")
26+
}
27+
return string(data[1 : 1+length]), 1 + length, nil
28+
}
29+
30+
func IsValidAddressHashMode(mode stacks.AddressHashMode) bool {
31+
return mode == stacks.AddressHashModeSerializeP2PKH ||
32+
mode == stacks.AddressHashModeSerializeP2WPKH
33+
}
34+
35+
func IsValidPubKeyEncoding(encoding stacks.PubKeyEncoding) bool {
36+
return encoding == stacks.PubKeyEncodingCompressed || encoding == stacks.PubKeyEncodingUncompressed
37+
}
38+
39+
func IsCompatibleHashModeAndKeyEncoding(hashMode stacks.AddressHashMode, keyEncoding stacks.PubKeyEncoding) bool {
40+
// P2WPKH and P2WSH only support compressed public keys
41+
if (hashMode == stacks.AddressHashModeSerializeP2WPKH) &&
42+
keyEncoding != stacks.PubKeyEncodingCompressed {
43+
return false
44+
}
45+
return true
46+
}

pkg/c32/c32.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ func SerializeAddress(address string) ([]byte, error) {
7272
var version byte
7373
switch address[0] {
7474
case 'S':
75-
version = 22 // Mainnet single-sig
75+
version = byte(stacks.AddressVersionMainnetSingleSig)
7676
case 'T':
77-
version = 26 // Testnet single-sig
77+
version = byte(stacks.AddressVersionTestnetSingleSig)
7878
default:
7979
return nil, fmt.Errorf("invalid address version: %c", address[0])
8080
}

pkg/c32/c32_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"testing"
77
)
88

9-
func TestCrockfordDecode(t *testing.T) {
9+
func TestC32Decode(t *testing.T) {
1010
tests := []struct {
1111
name string
1212
input string
@@ -37,14 +37,14 @@ func TestCrockfordDecode(t *testing.T) {
3737
t.Run(tt.name, func(t *testing.T) {
3838
decoded, err := C32Decode(tt.input)
3939
if (err != nil) != tt.wantErr {
40-
t.Errorf("CrockfordDecode() error = %v, wantErr %v", err, tt.wantErr)
40+
t.Errorf("C32Decode() error = %v, wantErr %v", err, tt.wantErr)
4141
return
4242
}
4343

4444
gotHex := hex.EncodeToString(decoded)
4545

4646
if !reflect.DeepEqual(gotHex, tt.expected) {
47-
t.Errorf("CrockfordDecode() = %v, want %v", gotHex, tt.expected)
47+
t.Errorf("C32Decode() = %v, want %v", gotHex, tt.expected)
4848
}
4949
})
5050
}

pkg/clarity/types_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,18 @@ func TestUIntBounds(t *testing.T) {
302302
assert.Error(t, err)
303303
})
304304
}
305+
306+
func TestAddressSerialization(t *testing.T) {
307+
address := "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.test-contract"
308+
expectedSerialized := "061a6d78de7b0625dfbfc16c3a8a5735f6dc3dc3f2ce0d746573742d636f6e7472616374"
309+
310+
addr, err := StringToPrincipal(address)
311+
assert.NoError(t, err)
312+
313+
serialized, err := addr.Serialize()
314+
assert.NoError(t, err)
315+
316+
serializedHex := hex.EncodeToString(serialized)
317+
318+
assert.Equal(t, expectedSerialized, serializedHex)
319+
}

pkg/crypto/key.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package crypto
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/tyler-smith/go-bip32"
7+
"github.com/tyler-smith/go-bip39"
8+
)
9+
10+
func DeriveStxPrivateKey(mnemonic string, index uint32) ([]byte, error) {
11+
seed := bip39.NewSeed(mnemonic, "")
12+
masterKey, err := bip32.NewMasterKey(seed)
13+
if err != nil {
14+
return nil, fmt.Errorf("failed to create master key: %w", err)
15+
}
16+
17+
path := []uint32{
18+
44 + bip32.FirstHardenedChild, // Purpose
19+
5757 + bip32.FirstHardenedChild, // Coin type (Stacks)
20+
0 + bip32.FirstHardenedChild, // Account
21+
0, // Change (external chain)
22+
index, // Address index
23+
}
24+
25+
key := masterKey
26+
for _, childIndex := range path {
27+
key, err = key.NewChildKey(childIndex)
28+
if err != nil {
29+
return nil, fmt.Errorf("failed to derive child key: %w", err)
30+
}
31+
}
32+
33+
privateKey := key.Key
34+
35+
compressedPrivKey := make([]byte, 33)
36+
copy(compressedPrivKey, privateKey)
37+
compressedPrivKey[32] = 0x01
38+
39+
return compressedPrivKey, nil
40+
}

pkg/crypto/key_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package crypto
2+
3+
import (
4+
"encoding/hex"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestDeriveStxPrivateKey(t *testing.T) {
11+
testCases := []struct {
12+
name string
13+
mnemonic string
14+
index uint32
15+
expected string
16+
}{
17+
{
18+
name: "Test Vector 1",
19+
mnemonic: "sound idle panel often situate develop unit text design antenna vendor screen opinion balcony share trigger accuse scatter visa uniform brass update opinion media",
20+
index: 0,
21+
expected: "8721c6a5237f5e8d361161a7855aa56885a3e19e2ea6ee268fb14eabc5e2ed9001",
22+
},
23+
}
24+
25+
for _, tc := range testCases {
26+
t.Run(tc.name, func(t *testing.T) {
27+
derivedKey, err := DeriveStxPrivateKey(tc.mnemonic, tc.index)
28+
assert.NoError(t, err)
29+
assert.Equal(t, tc.expected, hex.EncodeToString(derivedKey))
30+
})
31+
}
32+
}

0 commit comments

Comments
 (0)