Order Platform

Endless Go SDK Quick Start

1. Order Platform Move Contract

1.1 Contract Overview

The contract provides a decentralized framework for creating and managing orders within an e-commerce ecosystem. It defines the complete lifecycle of an order, from creation to completion, including payment, shipping, and cancellation. The system is built on the Endless blockchain, utilizing EndlessCoin for payments and a table-based storage system for scalability.

1.2 Core Concepts

1.2.1 Order Lifecycle (State Machine)

The core of the contract is a state machine that represents the lifecycle of an order. Each order progresses through a series of statuses, and transitions are governed by specific rules and function calls.

The statuses are defined as constants:

  • STATUS_PENDING_PAYMENT (0): The initial state after an order is created. Awaiting payment from the buyer.

  • STATUS_PAID (1): The buyer has successfully transferred the funds to the seller.

  • STATUS_SHIPPED (2): The seller has marked the order as shipped.

  • STATUS_COMPLETED (3): The buyer has confirmed receipt of the item. (Note: The function for this is currently commented out but is part of the intended design).

  • STATUS_CANCELLED (4): The order has been cancelled by either the buyer or the seller under specific conditions.

A typical successful order flow is: PENDING_PAYMENT -> PAID -> SHIPPED -> COMPLETED

1.2.2 Roles

There are two primary roles in this system:

  • Buyer: The user who creates and pays for the order.

  • Seller: The user who receives payment and ships the item.

Each state-changing function includes assertions to ensure that the transaction signer has the correct role for the intended action (e.g., only the buyer can pay, only the seller can ship).

1.2.3 Economy

All financial transactions are conducted using the native EndlessCoin(EDS). Prices and payments are specified in veins, the smallest unit of EndlessCoin, to ensure precision and avoid floating-point issues. 1 EDS = 100_000_000 Veins.

1.3 Data Structures (On-Chain State)

The contract's state is managed by two singleton resources, stored under the deployer's account (@order_platform).

1.3.1 Resources

  • OrderCounter:

    • A singleton resource that ensures every order receives a unique, sequential ID.

    • next_order_id: u64: A counter that is incremented each time a new order is created.

  • Orders:

    • A singleton resource that acts as the central repository for all orders.

    • orders: Table<u64, Order>: A scalable map (Table) that links a unique order_id (u64) to its corresponding Order struct. This allows for efficient lookups, insertions, and modifications.

1.3.2 Structs

  • Order:

    • The primary data structure representing an individual order. It has store and drop abilities, meaning it can be stored in global storage but cannot be copied.

    • order_id: u64: Unique identifier.

    • buyer: address: The address of the buyer.

    • seller: address: The address of the seller.

    • item_id: String: A string identifier for the product.

    • quantity: u128: The number of items ordered.

    • unit_price: u128: The price per item in Veins.

    • total_price: u128: The calculated total price (quantity * unit_price).

    • status: u8: The current status of the order, corresponding to the status constants.

  • OrderData:

    • A read-only representation of an order's data. It has copy, drop, and store abilities, making it suitable for returning from #[view] functions. Its structure mirrors the Order struct.

1.4 Events

The contract emits events at key points in the order lifecycle. This allows off-chain services, indexers, and user interfaces to monitor and react to on-chain activity without needing to query the state directly.

  • OrderCreatedEvent: Emitted when create_order is successfully called.

  • OrderPaidEvent: Emitted when pay_order is successfully called.

  • OrderShippedEvent: Emitted when ship_order is successfully called.

  • OrderCancelledEvent: Emitted when cancel_order is successfully called.

  • OrderCompletedEvent: Emitted when an order is marked as complete.

1.5 Functions (Module API)

1.5.1 Initialization Function

  • init_module(deployer: &signer):

    • A private function that must be called once by the module deployer to initialize the OrderCounter and Orders resources.

    • It includes an assertion to prevent re-initialization.

1.5.2 Entry Functions (State-Changing)

  • create_order(...):

    • Allows a buyer to create a new order.

    • It calculates the total_price, assigns a new order_id, stores the Order struct in the Orders table, and emits an OrderCreatedEvent.

  • pay_order(...):

    • Allows the buyer to pay for an order that is in the STATUS_PENDING_PAYMENT.

    • Preconditions: The order must exist, the signer must be the buyer, the status must be PENDING_PAYMENT, and the payment amount must match the total_price.

    • It transfers EndlessCoin from the buyer to the seller, updates the order status to STATUS_PAID, and emits an OrderPaidEvent.

  • ship_order(...):

    • Allows the seller to mark a paid order as shipped.

    • Preconditions: The order must exist, the signer must be the seller, and the status must be STATUS_PAID.

    • It updates the order status to STATUS_SHIPPED and emits an OrderShippedEvent.

  • cancel_order(...):

    • Allows a buyer or seller to cancel an order based on specific rules.

    • Preconditions:

      • A buyer can cancel if the status is PENDING_PAYMENT or PAID.

      • A seller can cancel only if the status is PENDING_PAYMENT.

      • The signer must be either the buyer or the seller.

    • It updates the order status to STATUS_CANCELLED and emits an OrderCancelledEvent.

    • Note: This function does not currently handle refund logic for paid cancellations. This would be a required enhancement for a production system.

1.5.3 View Functions (Read-Only)

  • get_order_details(order_id: u64): OrderData:

    • A public, read-only function that returns the OrderData for a given order_id.

    • It asserts that the order exists.

  • get_order_status(order_id: u64): u8:

    • A public, read-only function that returns just the status of a given order_id.

1.6 Error Handling

The module defines a set of comprehensive error codes to provide clear reasons for transaction failures.

  • E_NOT_INITIALIZED (1): The module has not been initialized.

  • E_ORDER_NOT_FOUND (2): The specified order_id does not exist.

  • E_INVALID_ORDER_STATUS (3): The action is not allowed in the order's current state.

  • E_INSUFFICIENT_FUNDS (4): The buyer does not have enough coins to pay.

  • E_SENDER_NOT_BUYER (5): The transaction was signed by an address other than the buyer's.

  • E_SENDER_NOT_SELLER (6): The transaction was signed by an address other than the seller's.

  • E_PAYMENT_AMOUNT_MISMATCH (7): The payment amount does not match the order's total price.

  • E_COIN_TRANSFER_FAILED (9): A generic error for coin transfer failures.

  • E_NOT_AUTHORIZED (10): The signer is neither the buyer nor the seller for a restricted action.

1.7 Future Enhancements

  • Implement complete_order: Add the entry function to allow buyers to mark an order as STATUS_COMPLETED.

  • Refund on Cancellation: Implement logic in cancel_order to automatically refund the buyer if a PAID order is cancelled.

  • Reputation System: Add a mechanism for buyers and sellers to rate each other after an order is completed.

  • Dispute Resolution: Introduce a mechanism for handling disputes, potentially involving a trusted third party or a decentralized arbitration process.

1.8 Deploy Contract

1.8.1 Create project folder

{path}>mkdir endless-orders
{path}>cd endless-orders

1.8.2 Create move contract folder

{path}\endless-orders>mkdir move
{path}\endless-orders>cd move

1.8.3 Init contract project

{path}\endless-orders\move>endless move init --name endless-orders
Success
{path}\endless-orders\move>dir
2025/07/02  11:14    <DIR>          .
2025/07/02  11:13    <DIR>          ..
2025/07/02  11:14               253 Move.toml
2025/07/02  11:14    <DIR>          scripts
2025/07/02  11:14    <DIR>          sources
2025/07/02  11:14    <DIR>          tests

1.8.4 Implementing the contract according to the above design

The complete contract is in the file {path}\endless-orders\move\sources\orders.move

1.8.5 Craete contract deplay account

{path}\endless-orders\move>endless init
Configuring for profile default
Choose network from [testnet, mainnet, local, custom | defaults to testnet]

No network given, using testnet...
Enter your private key as a hex literal (0x...) [Current: None | No input: Generate new key (or keep one if present)]

No key given, generating key...
Account 0xdcf462ef1bad8ed0901043126048acdbf6446f486eee2a38a12ddf8b727a2cc6 doesn't exist, creating it and funding it with 100000000 Veins
Account 0xdcf462ef1bad8ed0901043126048acdbf6446f486eee2a38a12ddf8b727a2cc6 funded successfully

---
Endless CLI is now set up for account 0xdcf462ef1bad8ed0901043126048acdbf6446f486eee2a38a12ddf8b727a2cc6 as profile default!  Run `endless --help` for more information about commands
Success

1.8.6 Modify contract address

Modify [addresses] field in {path}\endless-orders\move\Move.toml

[addresses]
order_platform = "FsWq9ZonkCHLZfoRG3Ci8PGr7ps3Mo5mqLGyE1CyRNhb"

"0xdcf462ef1bad8ed0901043126048acdbf6446f486eee2a38a12ddf8b727a2cc6"'s base58 format.

1.8.7 Deploy contract

{path}\endless-orders\move>endless move publish
Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY EndlessFramework
INCLUDING DEPENDENCY EndlessStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING endless-orders
package size 5577 bytes
Do you want to submit a transaction for a range of [402000 - 603000] Veins at a gas unit price of 100 Veins? [yes/no] >
yes
transaction_hash:8xsoY7xrNNXuV4t1CSiyECFoJD1YMjw9fHwZejxzu4gu
gas_used:4020
gas_unit_price:100
sender:dcf462ef1bad8ed0901043126048acdbf6446f486eee2a38a12ddf8b727a2cc6
sequence_number:0
success:true
timestamp_us:1751426811613310
version:250249525
vm_status:Executed successfully

Follow the prompts: type yes when asked for a Do you want to submit a transaction for a range of... and press Enter.

2. Use Go SDK to interact with contracts

Before you begin, please ensure that you have the Go language environment and VS Code installed on your system.

2.1 Install Go

  1. Download Go: Visit the official Go download page, download and install the latest version of Go for your operating system.

  2. Configure Environment Variables: The installer usually configures the environment variables automatically. If not, manually add the Go bin directory (e.g., C:\Go\bin or /usr/local/go/bin) to your system's PATH environment variable.

  3. Verify Installation: Open a terminal or command prompt and enter the following command to verify that Go is installed correctly:

    {path}\endless-orders>go version
    go version go1.24.4 windows/amd64

    If you see the version number output, the installation was successful.

2.2 Install Visual Studio Code

Visit the official VS Code website to download and install the version suitable for your operating system.

2.2.1 Install the Go Extension

VS Code supports development in different languages through extensions. For Go, we need to install the official Go extension.

  1. Open VS Code.

  2. Click the Extensions icon in the sidebar or use the shortcut Ctrl+Shift+X.

  3. Enter Go in the search box.

  4. Find the official extension published by the Go Team at Google and click Install.

Install Go Extension

2.2..2 Install Go Development Tools

The Go extension relies on some command-line tools to provide advanced features like code completion, formatting, definition jumping, and debugging (e.g., gopls, dlv).

  1. After installing the Go extension, a notification will usually appear in the bottom right corner suggesting you install these dependency tools. Click Install All.

  2. If you miss the notification, you can also install them manually:

    • Open the Command Palette: Ctrl+Shift+P (Windows/Linux) or Cmd+Shift+P (macOS).

    • Type Go: Install/Update Tools and press Enter.

    • Check all the tools in the list, then click OK.

    VS Code will automatically execute the go install command in the terminal to install these tools. Please ensure your network can access Go's domains.

2.3 Create Your Go Project

2.3.1 Initialize a Go Module

Since Go 1.11, Go Modules are the official way to manage project dependencies. Run the following command in the project root directory to initialize a module:

# Replace "endless-orders" with your module path, which is usually your code repository address
{path}\endless-orders>go mod init endless-orders
go: creating new go.mod: module endless-orders

After execution, a go.mod file will be generated in the directory.

2.3.2 Create the main.go File

Create a file named main.go in the project root directory and enter the following "Hello, World!" code:

package main

import "fmt"

func main() {
    fmt.Println("Hello, Endless!")
}

2.3.3 Add endless-go-sdk and cobra

In order to call the endless on-chain contract and conveniently process command line instructions, we need to add dependencies endless-go-sdk and cobra.

go get -u github.com/endless-labs/endless-go-sdk

To facilitate the processing of command line commands, we added cobra package.

go get -u github.com/spf13/cobra

2.3.4 Create project structure directories and files

endless-orders
│─main.go

├─cmd
│	│─ order.go
│	│─ root.go

└─web3
	│─ web3.go

main.go: program entry root.go: root Command order.go: All cli commands and parameter definitions and parsing of each command web3.go: All commands and interactions with on-chain contracts

2.3.5 Register root command

Definition a rootCmd in cmd/root.go

package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
	Use:   "order-cli",
	Short: "A CLI to interact with the order_platform::orders Move contract",
	Long:  `A command-line interface to create, manage, and query orders on the Endless blockchain.`,
	CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "Whoops. There was an error while executing your CLI '%s'", err)
		os.Exit(1)
	}
}

Then we can call its Execute() function in main.go as as follows

package main

import "endless-orders/cmd"

func main() {
	cmd.Execute()
}

2.3.6 Create an account and faucet EDS on testnet

2.3.6.1 Create an account

An account is essentially a public-private key pair and an account address derived from them. We save the generated private key and account address locally, so that when needed, we can directly read the file and construct an account to interact with the chain.

  • 1 Define and register the Create account command In cmd/order.go define and register the command, they call the corresponding specific implementation in web3/web3.go to complete the entire function.

// order.go
package cmd

import (
	"endless-orders/web3"
	"fmt"
	"strconv"

	"github.com/spf13/cobra"
)

// Create Account Command
var createAccountCmd = &cobra.Command{
	Use:   "create_account [account-name]",
	Short: "Creates a new account",
	Long:  `Creates a new account and saves it locally.`,
	Args:  cobra.ExactArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		accountName := args[0]
		web3.CreateAccount(accountName)
	},
}

func init() {
	// Add the order command to the root command
	rootCmd.AddCommand(createAccountCmd)
}
  • 2 Implement the Create account command

// web3.go
package web3

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"math/big"
	"os"
	"sync"

	"github.com/endless-labs/endless-go-sdk"
	"github.com/endless-labs/endless-go-sdk/bcs"
	"github.com/endless-labs/endless-go-sdk/crypto"
)

// AccountData holds the information for a saved account
type AccountData struct {
	PrivateKey string `json:"private_key"`
	Address    string `json:"address"`
}


func CreateAccount(accountName string) {
	// Create a new Ed25519 account
	account, err := endless.NewEd25519Account()
	if err != nil {
		panic("Failed to create account: " + err.Error())
	}

	// Save the account to a file
	privateKeyBytes := account.Signer.(*crypto.Ed25519PrivateKey).Bytes()

	data := AccountData{
		PrivateKey: hex.EncodeToString(privateKeyBytes),
		Address:    account.Address.String(),
	}

	fileContent, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		panic("Failed to marshal account data: " + err.Error())
	}

	err = os.WriteFile(accountName+".json", fileContent, 0644)
	if err != nil {
		panic("Failed to save account file: " + err.Error())
	}

	fmt.Printf("Account '%s' created.\n", accountName)
	fmt.Printf("Address: %s\n", account.Address.String())
	fmt.Printf("Saved to %s.json\n", accountName)
}

In this way, we have implemented a function to create an account and save it.

  • 3 Create an account named alice

{path}\endless-orders>go build
{path}\endless-orders>endless-orders.exe create_account alice
Account 'alice' created.
Address: GVFJPNkqkdH9hx5CwFGnFPUFzbDUaBjdCB3U8WkKrce3
Saved to alice.json

2.3.6.2 faucet EDS on testnet for alice

  • 1 Define and register the Faucet Account command Add the following code to the cmd/order.go

// Faucet Account Command
var faucetAccountCmd = &cobra.Command{
	Use:   "faucet_account [account-name]",
	Short: "Funds an account from the faucet",
	Long:  `Requests funds from the faucet for a given account.`,
	Args:  cobra.ExactArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		accountName := args[0]
		web3.FaucetAccount(accountName)
	},
}

Add faucetAccountCmd to init function.

// Add the order command to the root command  
  rootCmd.AddCommand(createAccountCmd)
  rootCmd.AddCommand(faucetAccountCmd)
  • 2 Implement the Faucet account command In order to easy for loading of an account, we implement a function in web3/web3.go

func LoadAccount(accountName string) *endless.Account {
	// Load the account from the file
	fileContent, err := os.ReadFile(accountName + ".json")
	if err != nil {
		panic("Failed to read account file: " + err.Error())
	}

	var data AccountData
	err = json.Unmarshal(fileContent, &data)
	if err != nil {
		panic("Failed to unmarshal account data: " + err.Error())
	}

	privateKeyBytes, err := hex.DecodeString(data.PrivateKey)
	if err != nil {
		panic("Failed to decode private key: " + err.Error())
	}
	privateKey := &crypto.Ed25519PrivateKey{}
	err = privateKey.FromBytes(privateKeyBytes)
	if err != nil {
		panic("privateKey key is not valid")
	}
	account, err := endless.NewAccountFromSigner(privateKey)
	if err != nil {
		panic("NewAccountFromSigner is not valid")
	}
	return account
}

To interact with the blockchain, you need to build an endless.Client object. The endless.Client provides a Faucet function, which can directly get a EDS on the test network. To send a transaction, you need to pass in the SequenceNumber of the account. The new account does not exist on the chain and has no SequenceNumber. Therefore, err = client.Faucet(*account) will directly report an error. In this case, you need to set the SequenceNumber of the account to 0.

func FaucetAccount(accountName string) {
	// Load the account from the file
	account := LoadAccount(accountName)
	// Create a client for Endless
	client, err := endless.NewClient(endless.TestnetConfig)
	if err != nil {
		panic("Failed to create client:" + err.Error())
	}

	// Fund the account from the faucet
	// err = client.Faucet(*account, endless.SequenceNumber(0)) // Use the sequence number to skip fetching it
	// Use the sequence number to skip fetching it
	err = client.Faucet(*account)
	if err != nil {
		pre_err := err
		err = client.Faucet(*account, endless.SequenceNumber(0))
		if err != nil {
			panic("Failed to fund account:" + pre_err.Error())
		}
	}

	Balance, err := client.AccountEDSBalance(account.Address)
	if err != nil {
		panic("Failed to get account balance:" + err.Error())
	}

	fmt.Printf("Successfully funded account '%s' from the faucet '%s'.\n", accountName, Balance)

}

At this point, all the functions of Faucet account are completed

  • 3 Faucet EDS on testnet for alice

{path}\endless-orders>go build
{path}\endless-orders>endless-orders.exe faucet_account alice
Successfully funded account 'alice' from the faucet '1000000000'.

2.3.7 Call the function of the published contract

So far, we have not interacted with the contract we published. Now let's create an order using our contract.

2.3.7.1 Create order subcommand

We put all interactive commands with the orders contract to the order subcommand. First define a orderCmd in cmd/order.go

var orderCmd = &cobra.Command{
	Use:   "order",
	Short: "Manage orders",
	Long:  `The main command to manage orders on the order platform.`,
}

Then register orderCmd to rootCmd

func init() {
	// Add the order command to the root command
	rootCmd.AddCommand(orderCmd)
	rootCmd.AddCommand(createAccountCmd)
	rootCmd.AddCommand(faucetAccountCmd)
}

2.3.7.2 Implement Create Order Command for order subcommand

  • 1 Define and register createOrderCmd for orderCmd in cmd/order.go

// Create Order Command
var createOrderCmd = &cobra.Command{
	Use:   "create",
	Short: "Creates a new order",
	Long:  `Creates a new order with the status PENDING_PAYMENT.`,
	Run: func(cmd *cobra.Command, args []string) {
		buyerName, _ := cmd.Flags().GetString("buyer")
		sellerName, _ := cmd.Flags().GetString("seller-name")
		itemId, _ := cmd.Flags().GetString("item-id")
		quantity, _ := cmd.Flags().GetUint64("quantity")
		unitPrice, _ := cmd.Flags().GetUint64("unit-price")
		web3.CreateOrder(buyerName, sellerName, itemId, quantity, unitPrice)
	},
}
func init() {

  // Add subcommands to the order command
	orderCmd.AddCommand(createOrderCmd)

	// Add flags for the create command
	createOrderCmd.Flags().String("buyer", "", "Address of the buyer (signer)")
	createOrderCmd.Flags().String("seller-name", "", "Address of the seller")
	createOrderCmd.Flags().String("item-id", "", "ID of the item being ordered")
	createOrderCmd.Flags().Uint64("quantity", 0, "Quantity of the item to order")
	createOrderCmd.Flags().Uint64("unit-price", 0, "Price per unit of the item")

	createOrderCmd.MarkFlagRequired("buyer")
	createOrderCmd.MarkFlagRequired("seller-name")
	createOrderCmd.MarkFlagRequired("item-id")
	createOrderCmd.MarkFlagRequired("quantity")
	createOrderCmd.MarkFlagRequired("unit-price")

	// Add the order command to the root command
	rootCmd.AddCommand(orderCmd)
	rootCmd.AddCommand(createAccountCmd)
	rootCmd.AddCommand(faucetAccountCmd)
}
  • 2 Define contract address and module address In endless, one address can publish multiple contracts, and each contract can be called a module. Such as 0x4 address is EndlessToken package. It contains Six modules, including coin,collection,nft,property_map,royalty and token. EndlessToken package

Define endless-orders address and orders module in web3/web3.go.

func GetContractAddress() *endless.AccountAddress {
	once.Do(func() {
		a := &endless.AccountAddress{}
		addrErr = a.ParseStringRelaxed("FsWq9ZonkCHLZfoRG3Ci8PGr7ps3Mo5mqLGyE1CyRNhb")
		if addrErr == nil {
			addr = a
		} else {
			panic("GetContractAddress Faild.")
		}
	})
	return addr
}

var CONTRACT_MODULE = "orders"

Use GetContractAddress to get the contract address

  • 3 Implement createOrderCmd

Serialize Arguments: The function's arguments (quantity, unitPrice, itemId) are serialized into the BCS (Binary Canonical Serialization) format, which is required by the Endless blockchain.

  • quantity and unitPrice are converted to u128.

  • itemId is serialized as a string.

Construct Entry Function Payload: It builds an endless.EntryFunction struct. This struct specifies the target smart contract to call,

  • Module: The contract's on-chain address (retrieved via GetContractAddress) and the module name (orders).

  • Function: The name of the function to call (create_order).

  • Args: The serialized arguments for the function call: seller's address, item ID, quantity, and unit price.

func CreateOrder(buyerName, sellerName, itemId string, quantity, unitPrice uint64) {
	buyer := LoadAccount(buyerName)
	seller := LoadAccount(sellerName)
	fmt.Printf("Creating order for buyer: %s  seller:%s\n", buyer.Address.String(), seller.Address.String())

	quantity_u128, err := bcs.SerializeU128(*big.NewInt(int64(quantity)))
	if err != nil {
		panic("quantity format error.")
	}
	unitPrice_u128, err := bcs.SerializeU128(*big.NewInt(int64(unitPrice)))
	if err != nil {
		panic("quantity format error.")
	}

	itemId_bytes, err := bcs.SerializeSingle(func(ser *bcs.Serializer) {
		ser.WriteString(itemId)
	})
	if err != nil {
		panic("failed to serialize itemId.")
	}

	entryFunction := endless.EntryFunction{
		Module: endless.ModuleId{
			Address: *GetContractAddress(),
			Name:    CONTRACT_MODULE,
		},
		Function: "create_order",
		ArgTypes: []endless.TypeTag{},
		Args: [][]byte{
			seller.Address[:],
			itemId_bytes,
			quantity_u128,
			unitPrice_u128,
		},
	}

	client, err := endless.NewClient(endless.TestnetConfig)
	if err != nil {
		panic("Failed to create client:" + err.Error())
	}

	rawTxn, err := client.BuildTransaction(
		buyer.AccountAddress(),
		endless.TransactionPayload{
			Payload: &entryFunction,
		},
	)
	if err != nil {
		panic("Failed to build transaction:" + err.Error())
	}

	// 2. Simulate transaction (optional)
	// This is useful for understanding how much the transaction will cost
	// and to ensure that the transaction is valid before sending it to the network
	// This is optional, but recommended
	simulationResult, err := client.SimulateTransaction(rawTxn, buyer)
	if err != nil {
		panic("Failed to simulate transaction:" + err.Error())
	}
	fmt.Printf("\n================ Simulation ================\n")
	fmt.Printf("Gas unit price: %d\n", simulationResult[0].GasUnitPrice)
	fmt.Printf("Gas used: %d\n", simulationResult[0].GasUsed)
	fmt.Printf("Total gas fee: %d\n", simulationResult[0].GasUsed*simulationResult[0].GasUnitPrice)
	fmt.Printf("Status: %s\n", simulationResult[0].VmStatus)
	if !simulationResult[0].Success {
		panic("Simulate transaction error.")
	}

	// 3. Sign transaction
	signedTxn, err := rawTxn.SignedTransaction(buyer)
	if err != nil {
		panic("Failed to sign transaction:" + err.Error())
	}

	// 4. Submit transaction
	submitResult, err := client.SubmitTransaction(signedTxn)
	if err != nil {
		panic("Failed to submit transaction:" + err.Error())
	}
	txnHash := submitResult.Hash

	fmt.Printf("txnHash: %s\n", txnHash)

	// 5. Wait for the transaction to complete
	userTransaction, err := client.WaitForTransaction(txnHash)
	if err != nil {
		panic("Failed to wait for transaction:" + err.Error())
	}
	if !userTransaction.Success {
		panic("Failed to on chain success:" + userTransaction.VmStatus)
	}

}

In this way, we have completed the function of creating orders on the chain.

  • 4 Create an order on chain

{path}\endless-orders>go build
{path}\endless-orders>endless-orders.exe order create --buyer alice --seller-name bob --item-id "items-00001" --quantity 100 --unit-price 100000
Creating order for buyer: GVFJPNkqkdH9hx5CwFGnFPUFzbDUaBjdCB3U8WkKrce3  seller:C6nCccsGQ6A6JqoGPS8S7n8xA7YRphsibp2Ba1yZNk2f

================ Simulation ================
Gas unit price: 100
Gas used: 475
Total gas fee: 47500
Status: Executed successfully
txnHash: 0xcc8e12ce5a32ce1d0384254ac7ef44cfae3e241e4b2cf2802fa7cd88060e7962

Visit the Official Endless Scan Website,can find the event orders::OrderCreatedEvent,or you can Parse the UserTransaction returned by WaitForTransaction to extract this event. We can know the newly created order id is 1.

// FsWq9ZonkCHLZfoRG3Ci8PGr7ps3Mo5mqLGyE1CyRNhb::orders::OrderCreatedEvent
{
  "buyer": "GVFJPNkqkdH9hx5CwFGnFPUFzbDUaBjdCB3U8WkKrce3",
  "order_id": "1",
  "seller": "C6nCccsGQ6A6JqoGPS8S7n8xA7YRphsibp2Ba1yZNk2f",
  "total_price": "10000000"
}

2.3.7.3 Implementing Other Order Subcommands

Similar to the order creation process described in 2.3.7.2, you can implement other commands other order commands such as order payment, order shipping, etc., The complete functional implementation is already in cmd/order.go and web3/web3.go in the latest code repository. It is important to note that when Go language json parses the field type of the returned object, sometimes the type it parses is not the type you expected. That is why when calling the view function get_order_status, its return value is converted to float64 in Go. For example, when calling the get_order_status view function, its return value is treated as a float64 in Go.

Last updated