Skip to content

Commit 5df9172

Browse files
authored
Merge pull request #36 from bestmike007/feat/implement-post-conditions
feat: implement post conditions & support smart contract clarity version
2 parents f75a452 + c1b487c commit 5df9172

File tree

12 files changed

+679
-72
lines changed

12 files changed

+679
-72
lines changed

.github/workflows/go.yml

+13-13
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,31 @@ jobs:
1111
lint:
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@v3
14+
- uses: actions/checkout@v4
1515

1616
- name: Set up Go
17-
uses: actions/setup-go@v3
17+
uses: actions/setup-go@v5
1818
with:
1919
go-version: '1.23'
2020

2121
- name: golangci-lint
22-
uses: golangci/golangci-lint-action@v3
22+
uses: golangci/golangci-lint-action@v6
2323
with:
2424
version: latest
2525

2626
build:
2727
runs-on: ubuntu-latest
2828
needs: lint
2929
steps:
30-
- uses: actions/checkout@v3
30+
- uses: actions/checkout@v4
3131

3232
- name: Set up Go
33-
uses: actions/setup-go@v3
33+
uses: actions/setup-go@v5
3434
with:
3535
go-version: '1.23'
3636

3737
- name: Cache Go modules
38-
uses: actions/cache@v3
38+
uses: actions/cache@v4
3939
with:
4040
path: ~/go/pkg/mod
4141
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@@ -49,15 +49,15 @@ jobs:
4949
runs-on: ubuntu-latest
5050
needs: build
5151
steps:
52-
- uses: actions/checkout@v3
52+
- uses: actions/checkout@v4
5353

5454
- name: Set up Go
55-
uses: actions/setup-go@v3
55+
uses: actions/setup-go@v5
5656
with:
5757
go-version: '1.23'
5858

5959
- name: Cache Go modules
60-
uses: actions/cache@v3
60+
uses: actions/cache@v4
6161
with:
6262
path: ~/go/pkg/mod
6363
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@@ -76,20 +76,20 @@ jobs:
7676
runs-on: ubuntu-latest
7777
needs: build
7878
steps:
79-
- uses: actions/checkout@v3
79+
- uses: actions/checkout@v4
8080

8181
- name: Set up Go
82-
uses: actions/setup-go@v3
82+
uses: actions/setup-go@v5
8383
with:
8484
go-version: '1.23'
8585

8686
- name: Cache Go modules
87-
uses: actions/cache@v3
87+
uses: actions/cache@v4
8888
with:
8989
path: ~/go/pkg/mod
9090
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
9191
restore-keys: |
9292
${{ runner.os }}-go-
9393
9494
- name: Run Integration Tests
95-
run: go test -v ./test/integration/...
95+
run: go test -v ./test/integration/...

pkg/stacks/constants.go

+46-4
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@ const (
1010
type PayloadType byte
1111

1212
const (
13-
PayloadTypeTokenTransfer PayloadType = 0x00
14-
PayloadTypeSmartContract PayloadType = 0x01
15-
PayloadTypeContractCall PayloadType = 0x02
13+
PayloadTypeTokenTransfer PayloadType = 0x00
14+
PayloadTypeSmartContract PayloadType = 0x01
15+
PayloadTypeContractCall PayloadType = 0x02
16+
PayloadTypePoisonMicroblock PayloadType = 0x03
17+
PayloadTypeCoinbase PayloadType = 0x04
18+
PayloadTypeCoinbaseToAltRecipient PayloadType = 0x05
19+
PayloadTypeVersionedSmartContract PayloadType = 0x06
20+
PayloadTypeTenureChange PayloadType = 0x07
21+
PayloadTypeNakamotoCoinbase PayloadType = 0x08
1622
)
1723

1824
type AddressType byte
@@ -25,7 +31,9 @@ const (
2531
type AnchorMode uint8
2632

2733
const (
28-
AnchorModeOnChainOnly AnchorMode = 0x01
34+
AnchorModeOnChainOnly AnchorMode = 0x01
35+
AnchorModeOffChainOnly AnchorMode = 0x02
36+
AnchorModeAny AnchorMode = 0x03
2937
)
3038

3139
type TransactionVersion uint8
@@ -50,6 +58,31 @@ const (
5058
PostConditionTypeNonFungible PostConditionType = 0x02
5159
)
5260

61+
type PostConditionPrincipalType uint8
62+
63+
const (
64+
PostConditionPrincipalTypeOrigin PostConditionPrincipalType = 0x01
65+
PostConditionPrincipalTypeStandard PostConditionPrincipalType = 0x02
66+
PostConditionPrincipalTypeContract PostConditionPrincipalType = 0x03
67+
)
68+
69+
type FungibleConditionCode uint8
70+
71+
const (
72+
FungibleConditionCodeSentEq FungibleConditionCode = 0x01
73+
FungibleConditionCodeSentGt FungibleConditionCode = 0x02
74+
FungibleConditionCodeSentGe FungibleConditionCode = 0x03
75+
FungibleConditionCodeSentLt FungibleConditionCode = 0x04
76+
FungibleConditionCodeSentLe FungibleConditionCode = 0x05
77+
)
78+
79+
type NonFungibleConditionCode uint8
80+
81+
const (
82+
NonFungibleConditionCodeSent NonFungibleConditionCode = 0x10
83+
NonFungibleConditionCodeNotSent NonFungibleConditionCode = 0x11
84+
)
85+
5386
type AuthType uint8
5487

5588
const (
@@ -71,6 +104,15 @@ const (
71104
AddressVersionTestnetSingleSig AddressVersion = 26
72105
)
73106

107+
type ClarityVersion uint8
108+
109+
const (
110+
ClarityVersionUnspecified ClarityVersion = 0
111+
ClarityVersion1 ClarityVersion = 1
112+
ClarityVersion2 ClarityVersion = 2
113+
ClarityVersion3 ClarityVersion = 3
114+
)
115+
74116
type PubKeyEncoding uint8
75117

76118
const (

pkg/stacks_blockchain_api_client/test/api_transactions_test.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/transaction/builder.go

+10-4
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func MakeSTXTokenTransfer(
208208

209209
signer := deriveSigner(senderKey)
210210

211-
tx, err := NewTokenTransferTransaction(recipient, amount.Uint64(), memo, network.Version, network.ChainID, signer, 0, 0, stacks.AnchorModeOnChainOnly, stacks.PostConditionModeAllow)
211+
tx, err := NewTokenTransferTransaction(recipient, amount.Uint64(), memo, network.Version, network.ChainID, signer, 0, 0, stacks.PostConditionModeAllow, []PostCondition{})
212212
if err != nil {
213213
return nil, &CustomError{Message: "Failed to create transaction", Err: err}
214214
}
@@ -224,11 +224,14 @@ func MakeSTXTokenTransfer(
224224
func MakeContractDeploy(
225225
contractName string,
226226
codeBody string,
227+
clarityVersion stacks.ClarityVersion,
227228
network stacks.StacksNetwork,
228229
senderAddress string,
229230
senderKey []byte,
230231
fee *big.Int,
231232
nonce *big.Int,
233+
postConditionMode stacks.PostConditionMode,
234+
postConditions []PostCondition,
232235
) (*SmartContractTransaction, error) {
233236
if contractName == "" || codeBody == "" || len(senderKey) == 0 {
234237
return nil, &CustomError{Message: "Invalid parameters: contractName, codeBody, or senderKey are empty"}
@@ -239,13 +242,14 @@ func MakeContractDeploy(
239242
tx, err := NewSmartContractTransaction(
240243
contractName,
241244
codeBody,
245+
clarityVersion,
242246
network.Version,
243247
network.ChainID,
244248
signer,
245249
0,
246250
0,
247-
stacks.AnchorModeOnChainOnly,
248-
stacks.PostConditionModeAllow,
251+
postConditionMode,
252+
postConditions,
249253
)
250254
if err != nil {
251255
return nil, &CustomError{Message: "Failed to create transaction", Err: err}
@@ -269,14 +273,16 @@ func MakeContractCall(
269273
senderKey []byte,
270274
fee *big.Int,
271275
nonce *big.Int,
276+
postConditionMode stacks.PostConditionMode,
277+
postConditions []PostCondition,
272278
) (*ContractCallTransaction, error) {
273279
if contractAddress == "" || contractName == "" || functionName == "" || len(senderKey) == 0 {
274280
return nil, &CustomError{Message: "Invalid parameters: contractAddress, contractName, functionName, or senderKey are empty"}
275281
}
276282

277283
signer := deriveSigner(senderKey)
278284

279-
tx, err := NewContractCallTransaction(contractAddress, contractName, functionName, functionArgs, network.Version, network.ChainID, signer, 0, 0, stacks.AnchorModeOnChainOnly, stacks.PostConditionModeAllow)
285+
tx, err := NewContractCallTransaction(contractAddress, contractName, functionName, functionArgs, network.Version, network.ChainID, signer, 0, 0, postConditionMode, postConditions)
280286
if err != nil {
281287
return nil, &CustomError{Message: "Failed to create transaction", Err: err}
282288
}

pkg/transaction/builder_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,14 @@ func TestMakeContractDeploy(t *testing.T) {
135135
tx, err := MakeContractDeploy(
136136
tt.contractName,
137137
tt.codeBody,
138+
stacks.ClarityVersionUnspecified,
138139
*network,
139140
senderAddress,
140141
senderKey,
141142
tt.fee,
142143
tt.nonce,
144+
stacks.PostConditionModeAllow,
145+
[]PostCondition{},
143146
)
144147

145148
if tt.expectedErr {

pkg/transaction/payload.go

+28-8
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ type TokenTransferPayload struct {
2424
}
2525

2626
type SmartContractPayload struct {
27-
ContractName string
28-
CodeBody string
27+
ContractName string
28+
CodeBody string
29+
ClarityVersion stacks.ClarityVersion
2930
}
3031

3132
type ContractCallPayload struct {
@@ -60,7 +61,7 @@ func NewTokenTransferPayload(recipient string, amount uint64, memo string) (*Tok
6061
}, nil
6162
}
6263

63-
func NewSmartContractPayload(contractName string, codeBody string) (*SmartContractPayload, error) {
64+
func NewSmartContractPayload(contractName string, codeBody string, clarityVersion stacks.ClarityVersion) (*SmartContractPayload, error) {
6465
if err := validateContractName(contractName); err != nil {
6566
return nil, err
6667
}
@@ -70,8 +71,9 @@ func NewSmartContractPayload(contractName string, codeBody string) (*SmartContra
7071
}
7172

7273
return &SmartContractPayload{
73-
ContractName: contractName,
74-
CodeBody: codeBody,
74+
ContractName: contractName,
75+
CodeBody: codeBody,
76+
ClarityVersion: clarityVersion,
7577
}, nil
7678
}
7779

@@ -141,10 +143,15 @@ func (p *TokenTransferPayload) Deserialize(data []byte) (int, error) {
141143
}
142144

143145
func (p *SmartContractPayload) Serialize() ([]byte, error) {
144-
buf := make([]byte, 0, len(p.ContractName)+len(p.CodeBody)+6) // 1 + 1 + 4 bytes for headers
146+
buf := make([]byte, 0, len(p.ContractName)+len(p.CodeBody)+7) // 1 + 1 + 1 + 4 bytes for headers
145147

146148
// Payload type
147-
buf = append(buf, byte(stacks.PayloadTypeSmartContract))
149+
if p.ClarityVersion == stacks.ClarityVersionUnspecified {
150+
buf = append(buf, byte(stacks.PayloadTypeSmartContract))
151+
} else {
152+
buf = append(buf, byte(stacks.PayloadTypeVersionedSmartContract))
153+
buf = append(buf, byte(p.ClarityVersion))
154+
}
148155

149156
// Contract name
150157
if len(p.ContractName) > stacks.MaxStringLengthBytes {
@@ -163,11 +170,24 @@ func (p *SmartContractPayload) Serialize() ([]byte, error) {
163170
}
164171

165172
func (p *SmartContractPayload) Deserialize(data []byte) (int, error) {
166-
if len(data) < 2 || stacks.PayloadType(data[0]) != stacks.PayloadTypeSmartContract {
173+
payloadType := stacks.PayloadType(data[0])
174+
if len(data) < 2 ||
175+
(payloadType != stacks.PayloadTypeSmartContract && payloadType != stacks.PayloadTypeVersionedSmartContract) {
167176
return 0, errors.New("invalid smart contract payload")
168177
}
169178

170179
offset := 1
180+
if payloadType == stacks.PayloadTypeVersionedSmartContract {
181+
p.ClarityVersion = stacks.ClarityVersion(data[offset])
182+
if p.ClarityVersion != stacks.ClarityVersion1 &&
183+
p.ClarityVersion != stacks.ClarityVersion2 &&
184+
p.ClarityVersion != stacks.ClarityVersion3 {
185+
return 0, fmt.Errorf("unsupported clarity version: %d", p.ClarityVersion)
186+
}
187+
offset++
188+
} else {
189+
p.ClarityVersion = stacks.ClarityVersionUnspecified
190+
}
171191

172192
// Contract name
173193
nameLen := int(data[offset])

pkg/transaction/payload_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"testing"
66

77
"github.com/icon-project/stacks-go-sdk/pkg/clarity"
8+
"github.com/icon-project/stacks-go-sdk/pkg/stacks"
89
"github.com/stretchr/testify/assert"
910
)
1011

@@ -123,7 +124,7 @@ func TestSmartContractPayloadValidation(t *testing.T) {
123124

124125
for _, tt := range tests {
125126
t.Run(tt.name, func(t *testing.T) {
126-
_, err := NewSmartContractPayload(tt.contractName, tt.codeBody)
127+
_, err := NewSmartContractPayload(tt.contractName, tt.codeBody, stacks.ClarityVersionUnspecified)
127128
if tt.expectedError == "" {
128129
assert.NoError(t, err)
129130
} else {
@@ -140,7 +141,7 @@ func TestSmartContractPayloadSerializationAndDeserialization(t *testing.T) {
140141
(define-public (increment)
141142
(ok (var-set counter (+ (var-get counter) 1))))
142143
`
143-
payload, err := NewSmartContractPayload(contractName, codeBody)
144+
payload, err := NewSmartContractPayload(contractName, codeBody, stacks.ClarityVersionUnspecified)
144145
assert.NoError(t, err)
145146

146147
serialized, err := payload.Serialize()

0 commit comments

Comments
 (0)