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 }