fabric-samples/marketplace/chaincode-go/main.go
2023-05-16 22:44:54 +03:00

256 lines
8.2 KiB
Go

/*
* SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"fmt"
"strconv"
"encoding/base64"
"encoding/json"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
"github.com/hyperledger/fabric-contract-api-go/metadata"
"github.com/hyperledger/fabric-chaincode-go/shim"
)
type MarketplaceContract struct {
contractapi.Contract
}
type ListingItem struct {
TokenAddr string `json:"tokenAddr"`
Id string `json:"id"`
}
type Listing struct {
ListingId string `json:"listingId"`
Creator string `json:"creator"`
Price int `json:"price"`
CurrencyAddr string `json:"currencyAddr"`
}
type ListingCreated struct {
ListingId string `json:"listingId"`
Creator string `json:"creator"`
Price int `json:"price"`
CurrencyAddr string `json:"currencyAddr"`
Nfts []ListingItem `json:"nfts"`
Batches []ListingItem `json:"batches"`
Collections []ListingItem `json:"collections"`
}
type ListingItemSold struct {
ListingId string `json:"listingId"`
Creator string `json:"creator"`
Price int `json:"price"`
TokenAddr string `json:"tokenAddr"`
CurrencyAddr string `json:"currencyAddr"`
TokenId string `json:"tokenId"`
Buyer string `json:"buyer"`
}
type ListingCanceled struct {
ListingId string `json:"listingId"`
}
const listingPrefix = "listing"
func _readListing(ctx contractapi.TransactionContextInterface, listingId string) (*Listing, error) {
listingKey, err := ctx.GetStub().CreateCompositeKey(listingPrefix, []string{listingId})
if err != nil {
return nil, fmt.Errorf("failed to CreateCompositeKey %s: %v", listingId, err)
}
listingBytes, err := ctx.GetStub().GetState(listingKey)
if err != nil {
return nil, fmt.Errorf("failed to GetState %s: %v", listingId, err)
}
listing := new(Listing)
err = json.Unmarshal(listingBytes, listing)
if err != nil {
return nil, fmt.Errorf("failed to Unmarshal listingBytes: %v", err)
}
return listing, nil
}
func (c *MarketplaceContract) List(ctx contractapi.TransactionContextInterface, creator string, listingId string, currencyAddr string, nfts []ListingItem, batches []ListingItem, collections []ListingItem, price int) (*Listing, error) {
listing := new(Listing)
listing.ListingId = listingId
listing.Creator = creator
listing.Price = price
listing.CurrencyAddr = currencyAddr
listingKey, err := ctx.GetStub().CreateCompositeKey(listingPrefix, []string{listingId})
if err != nil {
return nil, fmt.Errorf("failed to CreateCompositeKey to nftKey: %v", err)
}
listingBytes, err := json.Marshal(listing)
if err != nil {
return nil, fmt.Errorf("failed to marshal listing: %v", err)
}
err = ctx.GetStub().PutState(listingKey, listingBytes)
if err != nil {
return nil, fmt.Errorf("failed to PutState listingBytes %s: %v", listingBytes, err)
}
listingCreated := new(ListingCreated)
listingCreated.ListingId = listingId
listingCreated.Creator = creator
listingCreated.Price = price
listingCreated.CurrencyAddr = currencyAddr
listingCreated.Nfts = nfts
listingCreated.Batches = batches
listingCreated.Collections = collections
listingCreatedBytes, err := json.Marshal(listingCreated)
if err != nil {
return nil, fmt.Errorf("failed to marshal listingCreatedBytes: %v", err)
}
err = ctx.GetStub().SetEvent("ListingCreated", listingCreatedBytes)
if err != nil {
return nil, fmt.Errorf("failed to SetEvent listingCreatedBytes %s: %v", listingCreatedBytes, err)
}
return listing, nil
}
func (c *MarketplaceContract) CancelListing(ctx contractapi.TransactionContextInterface, listingId string) (bool, error) {
sender64, err := ctx.GetClientIdentity().GetID()
if err != nil {
return false, fmt.Errorf("failed to GetClientIdentity: %v", err)
}
senderBytes, err := base64.StdEncoding.DecodeString(sender64)
if err != nil {
return false, fmt.Errorf("failed to DecodeString senderBytes: %v", err)
}
sender := string(senderBytes)
listing, err := _readListing(ctx, listingId);
if err != nil {
return false, fmt.Errorf("failed to _readListing: %v", err)
}
if (listing.Creator != sender) {
return false, fmt.Errorf("sender is not owner of listing")
}
listingKey, err := ctx.GetStub().CreateCompositeKey(listingPrefix, []string{listingId})
if err != nil {
return false, fmt.Errorf("failed to CreateCompositeKey to nftKey: %v", err)
}
err = ctx.GetStub().DelState(listingKey)
if err != nil {
return false, fmt.Errorf("failed to DelState %s: %v", listingKey, err)
}
listingCanceled := new(ListingCanceled)
listingCanceled.ListingId = listingId
listingCanceledBytes, err := json.Marshal(listingCanceled)
if err != nil {
return false, fmt.Errorf("failed to marshal listingCanceledBytes: %v", err)
}
err = ctx.GetStub().SetEvent("ListingCanceled", listingCanceledBytes)
if err != nil {
return false, fmt.Errorf("failed to SetEvent listingCanceledBytes %s: %v", listingCanceledBytes, err)
}
return true, nil
}
func (c *MarketplaceContract) Buy(ctx contractapi.TransactionContextInterface, buyer string, listingId string, tokenAddr string, tokenId string) (bool, error) {
listing, err := _readListing(ctx, listingId);
if err != nil {
return false, fmt.Errorf("failed to _readListing: %v", err)
}
// payment
stub := ctx.GetStub()
params := []string{"TransferFrom", buyer, listing.Creator, strconv.Itoa(listing.Price)}
invokeArgs := make([][]byte, len(params))
for i, arg := range params {
invokeArgs[i] = []byte(arg)
}
response := stub.InvokeChaincode(listing.CurrencyAddr, invokeArgs, "firefly")
if response.Status != shim.OK {
return false, fmt.Errorf("Failed to invoke erc-20 chaincode. Got error: %v %s %s %s", response, buyer, listing.Creator, strconv.Itoa(listing.Price))
}
// transfer tokens
tokenTransferParams := []string{"TransferFrom", listing.Creator, buyer, tokenId}
tokenTransferInvokeArgs := make([][]byte, len(tokenTransferParams))
for i, arg := range tokenTransferParams {
tokenTransferInvokeArgs[i] = []byte(arg)
}
tokenTransferResponse := stub.InvokeChaincode(tokenAddr, tokenTransferInvokeArgs, "firefly")
if tokenTransferResponse.Status != shim.OK {
return false, fmt.Errorf("Failed to invoke erc-721 chaincode. Got error: %s", tokenTransferResponse.Payload)
}
listingItemSold := new(ListingItemSold)
listingItemSold.ListingId = listingId
listingItemSold.Creator = listing.Creator
listingItemSold.Price = listing.Price
listingItemSold.CurrencyAddr = listing.CurrencyAddr
listingItemSold.TokenAddr = tokenAddr
listingItemSold.Buyer = buyer
listingItemSold.TokenId = tokenId
listingItemSoldBytes, err := json.Marshal(listingItemSold)
if err != nil {
return false, fmt.Errorf("failed to marshal listingItemSoldBytes: %v", err)
}
err = ctx.GetStub().SetEvent("ListingItemSold", listingItemSoldBytes)
if err != nil {
return false, fmt.Errorf("failed to SetEvent listingItemSoldBytes %s: %v", listingItemSoldBytes, err)
}
return true, nil
}
func main() {
marketplaceContract := new(MarketplaceContract)
marketplaceContract.Info.Version = "0.0.1"
marketplaceContract.Info.Description = "MarketplaceContract fabric port"
marketplaceContract.Info.License = new(metadata.LicenseMetadata)
marketplaceContract.Info.License.Name = "Apache-2.0"
marketplaceContract.Info.Contact = new(metadata.ContactMetadata)
marketplaceContract.Info.Contact.Name = "Marketplace"
chaincode, err := contractapi.NewChaincode(marketplaceContract)
chaincode.Info.Title = "Marketplace chaincode"
chaincode.Info.Version = "0.0.1"
if err != nil {
panic("Could not create chaincode from MarketplaceContract." + err.Error())
}
err = chaincode.Start()
if err != nil {
panic("Failed to start chaincode. " + err.Error())
}
}