Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package archethic

import (
"bytes"
"compress/flate"
"encoding/json"
"fmt"

"golang.org/x/exp/maps"
)

// Contract represents a WASM smart contract
type Contract struct {
Bytecode []byte
Manifest ContractManifest
}

// NewCompressedContract instanciates a new contract by compressing the bytecode
func NewCompressedContract(bytecode []byte, manifest ContractManifest) Contract {
var b bytes.Buffer
w, err := flate.NewWriter(&b, -1)
if err != nil {
panic(fmt.Sprintf("Cannot compress the bytecode %s", err.Error()))
}

w.Write(bytecode)
w.Close()

return Contract{
Bytecode: b.Bytes(),
Manifest: manifest,
}
}

func (c Contract) toBytes() []byte {
buf := []byte{1}
buf = append(buf, EncodeInt32(uint32(len(c.Bytecode)))...)
buf = append(buf, c.Bytecode...)
buf = append(buf, c.Manifest.toBytes()...)

return buf
}

// Actions returns the action of a contract
func (c Contract) Actions() []ContractAction {
actions := make([]ContractAction, 0)
for name, f := range c.Manifest.ABI.Functions {
if f.FunctionType == Action && f.TriggerType == TriggerTransaction {
actions = append(actions, ContractAction{
Name: name,
Parameters: maps.Keys(f.Input),
})
}
}

return actions
}

// ContractAction represents an overview of the contract's action
type ContractAction struct {
Name string
Parameters []string
}

// ContractManifest represents a manifest or specification of the contract used by clients & third-party
type ContractManifest struct {
ABI WasmABI `json:"abi"`
UpgradeOpts `json:"upgradeOpts"`
}

func (m ContractManifest) toBytes() []byte {
var iface map[string]interface{}

bytes, _ := json.Marshal(m)
json.Unmarshal(bytes, &iface)
bytes, err := SerializeTypedData(iface)
if err != nil {
panic("invalid manifest")
}
return bytes
}

// WasmABI represents the interface to communicate with the WASM binary defining functions and state types
type WasmABI struct {
State map[string]string `json:"state"`
Functions map[string]WasmFunctionABI `json:"functions"`
}

// WasmFunctionABI represent the specification and the interface of a function
type WasmFunctionABI struct {
FunctionType WasmFunctionType `json:"type"`
TriggerType WasmTriggerType `json:"triggerType"`
TriggerArgument string `json:"triggerArgument,omitempty"`
Input map[string]interface{} `json:"input"`
Output map[string]interface{} `json:"output"`
}

func (f WasmFunctionABI) toMap(name string) map[string]interface{} {
return map[string]interface{}{
"name": name,
"type": f.FunctionType,
"triggerType": f.TriggerType,
"triggerArgument": f.TriggerArgument,
"input": f.Input,
"output": f.Output,
}
}

// WasmFunctionType represents the type of function
type WasmFunctionType string

// WasmTrigger represents the type of a trigger to execute the function
type WasmTriggerType string

const (
// Action represents a function triggered by a transaction
Action WasmFunctionType = "action"

// PublicFunction represents a function called by anyone (readonly)
PublicFunction WasmFunctionType = "publicFunction"
)

const (
// TriggerTransaction represents an action triggered by a transaction
TriggerTransaction WasmTriggerType = "transaction"

// TriggerDateTime represents an action triggered by a datetime timestamp
TriggerDateTime WasmTriggerType = "datetime"

// TriggerInterval represents an action triggered by a cron interval
TriggerInterval WasmTriggerType = "interval"

// TriggerOracle represents an action triggered by an oracle's event
TriggerOracle WasmTriggerType = "oracle"
)

// UpgradeOpts represents the options to allow the upgrade of the contract
type UpgradeOpts struct {
From string `json:"from"`
}
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/archethic-foundation/libgo

go 1.20
go 1.22.0

toolchain go1.22.4

require (
github.com/aead/ecdh v0.2.0
Expand All @@ -9,8 +11,8 @@ require (
github.com/decred/dcrd/dcrec/secp256k1 v1.0.3
github.com/hasura/go-graphql-client v0.9.1
github.com/nshafer/phx v0.2.0
golang.org/x/crypto v0.6.0
github.com/ybbus/jsonrpc/v3 v3.1.4
golang.org/x/crypto v0.6.0
)

require (
Expand All @@ -19,6 +21,7 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
golang.org/x/sys v0.5.0 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
42 changes: 33 additions & 9 deletions transaction_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
type TransactionType uint8

const (
Version uint32 = 3
Version uint32 = 4
KeychainType TransactionType = 255
KeychainAccessType TransactionType = 254
TransferType TransactionType = 253
Expand All @@ -38,7 +38,7 @@ type TransactionBuilder struct {

type TransactionData struct {
Content []byte
Code []byte
Contract *Contract
Ledger Ledger
Ownerships []Ownership
Recipients []Recipient
Expand All @@ -47,8 +47,12 @@ type TransactionData struct {
func (t TransactionData) toBytes(tx_version uint32) []byte {
buf := make([]byte, 0)

// Encode code
buf = appendSizeAndContent(buf, t.Code, 32)
// Encode contract
if t.Contract == nil {
buf = append(buf, byte(0))
} else {
buf = append(buf, t.Contract.toBytes()...)
}

// Encode content
buf = appendSizeAndContent(buf, t.Content, 32)
Expand Down Expand Up @@ -247,8 +251,8 @@ func NewTransaction(txType TransactionType) *TransactionBuilder {
Version: Version,
TxType: txType,
Data: TransactionData{
Code: []byte{},
Content: []byte{},
Contract: nil,
Content: []byte{},
Ledger: Ledger{
Uco: UcoLedger{
Transfers: []UcoTransfer{},
Expand All @@ -267,8 +271,8 @@ func (t *TransactionBuilder) SetContent(content []byte) {
t.Data.Content = content
}

func (t *TransactionBuilder) SetCode(code string) {
t.Data.Code = []byte(code)
func (t *TransactionBuilder) SetContract(contract Contract) {
t.Data.Contract = &contract
}

func (t *TransactionBuilder) SetType(txType TransactionType) {
Expand Down Expand Up @@ -469,9 +473,29 @@ func (t *TransactionBuilder) ToJSONMap() (map[string]interface{}, error) {
"args": args,
}
}

var contract map[string]interface{} = nil

if t.Data.Contract != nil {
functionsWASMABI := make([]map[string]interface{}, 0)
for fun, f := range t.Data.Contract.Manifest.ABI.Functions {
functionsWASMABI = append(functionsWASMABI, f.toMap(fun))
}

contract = map[string]interface{}{
"bytecode": hex.EncodeToString(t.Data.Contract.Bytecode),
"manifest": map[string]interface{}{
"abi": map[string]interface{}{
"functions": functionsWASMABI,
"state": t.Data.Contract.Manifest.ABI.State,
},
},
}
}

data := map[string]interface{}{
"content": string(t.Data.Content),
"code": string(t.Data.Code),
"contract": contract,
"ownerships": ownerships,
"ledger": map[string]interface{}{
"uco": map[string]interface{}{
Expand Down
Loading