github.com/mavryk-network/mvgo@v1.19.9/contract/nft.go (about)

     1  // Copyright (c) 2020-2022 Blockwatch Data Inc.
     2  // Author: alex@blockwatch.cc
     3  
     4  package contract
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/mavryk-network/mvgo/mavryk"
    10  	"github.com/mavryk-network/mvgo/micheline"
    11  )
    12  
    13  type NftLedgerSchema byte
    14  
    15  const (
    16  	NftLedgerSchemaInvalid NftLedgerSchema = iota
    17  	NftLedgerSchema1
    18  	NftLedgerSchema2
    19  	NftLedgerSchema3
    20  )
    21  
    22  func (s NftLedgerSchema) IsValid() bool {
    23  	return s != NftLedgerSchemaInvalid
    24  }
    25  
    26  var nftLedgerSpecs = map[NftLedgerSchema]micheline.Prim{
    27  	// 1 @key: {0: address, 1: nat}     @value: nat
    28  	NftLedgerSchema1: micheline.NewPairType(
    29  		micheline.NewPairType(
    30  			micheline.NewCode(micheline.T_ADDRESS), // owner
    31  			micheline.NewCode(micheline.T_NAT),     // token_id
    32  		),
    33  		micheline.NewCode(micheline.T_NAT), // balance
    34  	),
    35  	// 2 @key: nat  @value: address
    36  	NftLedgerSchema2: micheline.NewPairType(
    37  		micheline.NewCode(micheline.T_NAT),     // token_id
    38  		micheline.NewCode(micheline.T_ADDRESS), // owner
    39  	),
    40  	// 3 @key: {0: nat, 1: address}     @value: nat
    41  	NftLedgerSchema3: micheline.NewPairType(
    42  		micheline.NewPairType(
    43  			micheline.NewCode(micheline.T_NAT),     // token_id
    44  			micheline.NewCode(micheline.T_ADDRESS), // owner
    45  		),
    46  		micheline.NewCode(micheline.T_NAT), // balance
    47  	),
    48  }
    49  
    50  // Detect NFT legder schema from bigmap key + value type.
    51  func DetectNftLedger(key, val micheline.Prim) NftLedgerSchema {
    52  	if !key.IsValid() || !val.IsValid() {
    53  		return NftLedgerSchemaInvalid
    54  	}
    55  	for t, v := range nftLedgerSpecs {
    56  		if !v.Args[0].IsEqual(key) {
    57  			continue
    58  		}
    59  		if !v.Args[1].IsEqual(val) {
    60  			continue
    61  		}
    62  		return t
    63  	}
    64  	return NftLedgerSchemaInvalid
    65  }
    66  
    67  type NftLedger struct {
    68  	Address    mavryk.Address
    69  	Schema     NftLedgerSchema
    70  	Bigmap     int64
    71  	FirstBlock int64
    72  }
    73  
    74  func (l NftLedger) DecodeEntry(prim micheline.Prim) (bal NftLedgerEntry, err error) {
    75  	bal.schema = l.Schema
    76  	err = prim.Decode(&bal)
    77  	return
    78  }
    79  
    80  type NftLedgerEntry struct {
    81  	Owner   mavryk.Address
    82  	TokenId mavryk.Z
    83  	Balance mavryk.Z
    84  	schema  NftLedgerSchema
    85  }
    86  
    87  func (b *NftLedgerEntry) UnmarshalPrim(prim micheline.Prim) error {
    88  	// schema switch to select the chosen struct type with custom struct tags
    89  	switch b.schema {
    90  	case NftLedgerSchema1:
    91  		return prim.Decode((*NftLedger1)(b))
    92  	case NftLedgerSchema2:
    93  		return prim.Decode((*NftLedgerTyp2)(b))
    94  	case NftLedgerSchema3:
    95  		return prim.Decode((*NftLedgerTyp3)(b))
    96  	default:
    97  		return fmt.Errorf("unsupported NFT ledger type %d", b.schema)
    98  	}
    99  }
   100  
   101  // 1 @key: {0: address, 1: nat}     @value: nat
   102  type NftLedger1 NftLedgerEntry
   103  
   104  func (b *NftLedger1) UnmarshalPrim(prim micheline.Prim) error {
   105  	var alias struct {
   106  		Owner   mavryk.Address `prim:"owner,path=0/0"`
   107  		TokenId mavryk.Z       `prim:"token_id,path=0/1"`
   108  		Balance mavryk.Z       `prim:"balance,path=1"`
   109  	}
   110  	err := prim.Decode(&alias)
   111  	if err == nil {
   112  		b.Owner = alias.Owner
   113  		b.TokenId = alias.TokenId
   114  		b.Balance = alias.Balance
   115  	}
   116  	return err
   117  }
   118  
   119  // 2 @key: nat  @value: address
   120  type NftLedgerTyp2 NftLedgerEntry
   121  
   122  func (b *NftLedgerTyp2) UnmarshalPrim(prim micheline.Prim) error {
   123  	var alias struct {
   124  		TokenId mavryk.Z       `prim:"token_id,path=0"`
   125  		Owner   mavryk.Address `prim:"owner,path=1"`
   126  	}
   127  	err := prim.Decode(&alias)
   128  	if err == nil {
   129  		b.Owner = alias.Owner
   130  		b.TokenId = alias.TokenId
   131  		b.Balance.SetInt64(1)
   132  	}
   133  	return err
   134  }
   135  
   136  // 3 @key: {0: nat, 1: address}     @value: nat
   137  type NftLedgerTyp3 NftLedgerEntry
   138  
   139  func (b *NftLedgerTyp3) UnmarshalPrim(prim micheline.Prim) error {
   140  	var alias struct {
   141  		TokenId mavryk.Z       `prim:"token_id,path=0/0"`
   142  		Owner   mavryk.Address `prim:"owner,path=0/1"`
   143  		Balance mavryk.Z       `prim:"balance,path=1"`
   144  	}
   145  	err := prim.Decode(&alias)
   146  	if err == nil {
   147  		b.Owner = alias.Owner
   148  		b.TokenId = alias.TokenId
   149  		b.Balance = alias.Balance
   150  	}
   151  	return err
   152  }