mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-23 01:55:10 +00:00
257 lines
7.6 KiB
Go
257 lines
7.6 KiB
Go
/*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/hyperledger/fabric-chaincode-go/shim"
|
|
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
|
"github.com/hyperledger/fabric-contract-api-go/metadata"
|
|
"strconv"
|
|
)
|
|
|
|
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()
|
|
|
|
if listing.Price > 0 {
|
|
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())
|
|
}
|
|
}
|