import { JSONPath } from 'jsonpath-plus' ; import { TezosMessageUtils } from '../../TezosMessageUtil' ; import { TezosNodeReader } from '../../TezosNodeReader' ; import { TezosNodeWriter } from '../../TezosNodeWriter' ; import { KeyStore , Signer } from '../../../../types/ExternalInterfaces' ; import * as TezosTypes from '../../../../types/tezos/TezosChainTypes' ; import { TezosContractUtils } from '../TezosContractUtils' ; import { TezosConstants } from '../../../../types/tezos/TezosConstants' ; interface SingleAssetSimpleStorage { administrator : string ; tokens : number ; ledger : number ; metadata : number ; paused : string ; operators : number ; tokenMetadata : number ; totalSupply : number ; } interface SingleAssetTokenDefinition { tokenid : number ; metadata : Record < string , string > ; } interface TransferPair { address : string ; amount : number ; } interface UpdateOperator { owner : string ; operator : string ; } /**
 * Interface for the FA2.0 contract implementation outlined here: https://gitlab.com/tzip/tzip/-/tree/master/proposals/tzip-12/tzip-12.md.
 *
 * Compatible with the contract as of May 24, 2021 from https://smartpy.io/ide?template=FA2.py
 */ export namespace SingleAssetTokenHelper { param server param address export async function verifyDestination ( server , address ) { return TezosContractUtils . verifyDestination ( server , address , '17aab0975df6139f4ff29be76a67f348' ) ; } param script export function verifyScript ( script ) { return TezosContractUtils . verifyScript ( script , '000' ) ; } param server param signer param keystore param fee param administrator param metadataUrl param pause param gas param freight export async function deployContract ( server , signer , keystore , fee , administrator , metadataUrl , pause = true , gas = 800000 , freight = 20000 ) { const contract = `parameter (or (or (pair %balance_of (list %requests (pair (address %owner) (nat %token_id))) (contract %callback (list (pair (pair %request (address %owner) (nat %token_id)) (nat %balance))))) (or (pair %mint (pair (address %address) (nat %amount)) (pair (map %metadata string bytes) (nat %token_id))) (address %set_administrator))) (or (or (pair %set_metadata (string %k) (bytes %v)) (bool %set_pause)) (or (list %transfer (pair (address %from_) (list %txs (pair (address %to_) (pair (nat %token_id) (nat %amount)))))) (list %update_operators (or (pair %add_operator (address %owner) (pair (address %operator) (nat %token_id))) (pair %remove_operator (address %owner) (pair (address %operator) (nat %token_id)))))))); 
        storage (pair (pair (pair (address %administrator) (nat %all_tokens)) (pair (big_map %ledger address nat) (big_map %metadata string bytes))) (pair (pair (big_map %operators (pair (address %owner) (pair (address %operator) (nat %token_id))) unit) (bool %paused)) (pair (big_map %token_metadata nat (pair (nat %token_id) (map %token_info string bytes))) (big_map %total_supply nat nat)))); 
        code { CAST (pair (or (or (pair (list (pair address nat)) (contract (list (pair (pair address nat) nat)))) (or (pair (pair address nat) (pair (map string bytes) nat)) address)) (or (or (pair string bytes) bool) (or (list (pair address (list (pair address (pair nat nat))))) (list (or (pair address (pair address nat)) (pair address (pair address nat))))))) (pair (pair (pair address nat) (pair (big_map address nat) (big_map string bytes))) (pair (pair (big_map (pair address (pair address nat)) unit) bool) (pair (big_map nat (pair nat (map string bytes))) (big_map nat nat))))); UNPAIR; IF_LEFT { IF_LEFT { SWAP; DUP; DUG 2; GET 3; CDR; IF { PUSH string "FA2_PAUSED"; FAILWITH; } {}; DUP; CAR; MAP { DUP 3; GET 5; SWAP; DUP; DUG 2; CDR; MEM; IF {} { PUSH string "FA2_TOKEN_UNDEFINED"; FAILWITH; }; DUP 3; CAR; GET 3; SWAP; DUP; DUG 2; CAR; MEM; IF { DUP 3; CAR; GET 3; SWAP; DUP; DUG 2; CAR; GET; IF_SOME { } { PUSH int 430; FAILWITH; }; SWAP; PAIR; } { PUSH nat 0; SWAP; PAIR; }; }; NIL operation; DIG 2; CDR; PUSH mutez 0; DIG 3; TRANSFER_TOKENS; CONS; } { IF_LEFT { SWAP; DUP; DUG 2; CAR; CAR; CAR; SENDER; COMPARE; EQ; IF {} { PUSH string "FA2_NOT_ADMIN"; FAILWITH; }; DUP; GET 4; PUSH nat 0; COMPARE; EQ; IF {} { PUSH string "single-asset: token-id <> 0"; FAILWITH; }; DUP; GET 4; DUP 3; CAR; CAR; CDR; COMPARE; EQ; IF {} { PUSH string "Token-IDs should be consecutive"; FAILWITH; }; SWAP; DUP; DUG 2; UNPAIR; UNPAIR; CAR; DIG 4; CAR; CAR; CDR; DUP; PUSH nat 1; DUP 7; GET 4; ADD; DUP; DUG 2; COMPARE; LE; IF { DROP; } { SWAP; DROP; }; SWAP; PAIR; PAIR; PAIR; DUP; DUG 2; CAR; GET 3; SWAP; DUP; DUG 2; CAR; CAR; MEM; IF { SWAP; UNPAIR; UNPAIR; SWAP; UNPAIR; DUP; DUP 6; CAR; CAR; DUP; DUG 2; GET; IF_SOME {} { PUSH int 541; FAILWITH; }; DUP 7; CAR; CDR; ADD; SOME; SWAP; UPDATE; PAIR; SWAP; PAIR; PAIR; SWAP; } { SWAP; UNPAIR; UNPAIR; SWAP; UNPAIR; DUP 5; CAR; CDR; SOME; DUP 6; CAR; CAR; UPDATE; PAIR; SWAP; PAIR; PAIR; SWAP; }; SWAP; DUP; DUG 2; GET 5; SWAP; DUP; DUG 2; GET 4; MEM; IF { DROP; } { SWAP; DUP; GET 5; DIG 2; DUP; GET 3; SWAP; DUP; DUG 4; GET 4; PAIR; SOME; DUP 4; GET 4; UPDATE; UPDATE 5; DUP; GET 6; DUP 3; CAR; CDR; SOME; DIG 3; GET 4; UPDATE; UPDATE 6; }; } { SWAP; DUP; DUG 2; CAR; CAR; CAR; SENDER; COMPARE; EQ; IF {} { PUSH string "FA2_NOT_ADMIN"; FAILWITH; }; SWAP; UNPAIR; UNPAIR; CDR; DIG 3; PAIR; PAIR; PAIR; }; NIL operation; }; } { IF_LEFT { IF_LEFT { SWAP; DUP; DUG 2; CAR; CAR; CAR; SENDER; COMPARE; EQ; IF {} { PUSH string "FA2_NOT_ADMIN"; FAILWITH; }; SWAP; UNPAIR; UNPAIR; SWAP; UNPAIR; SWAP; DUP 5; CDR; SOME; DIG 5; CAR; UPDATE; SWAP; PAIR; SWAP; PAIR; PAIR; } { SWAP; DUP; DUG 2; CAR; CAR; CAR; SENDER; COMPARE; EQ; IF {} { PUSH string "FA2_NOT_ADMIN"; FAILWITH; }; SWAP; UNPAIR; SWAP; UNPAIR; CAR; DIG 3; SWAP; PAIR; PAIR; SWAP; PAIR; }; } { IF_LEFT { SWAP; DUP; DUG 2; GET 3; CDR; IF { PUSH string "FA2_PAUSED"; FAILWITH; } {}; DUP; ITER { DUP; CDR; ITER { DUP; GET 3; PUSH nat 0; COMPARE; EQ; IF {} { PUSH string "single-asset: token-id <> 0"; FAILWITH; }; DUP 4; CAR; CAR; CAR; SENDER; COMPARE; EQ; IF { PUSH bool True; } { SENDER; DUP 3; CAR; COMPARE; EQ; }; IF { PUSH bool True; } { DUP 4; GET 3; CAR; SWAP; DUP; DUG 2; GET 3; SENDER; DUP 5; CAR; PAIR 3; MEM; }; IF {} { PUSH string "FA2_NOT_OPERATOR"; FAILWITH; }; DUP 4; GET 5; SWAP; DUP; DUG 2; GET 3; MEM; IF {} { PUSH string "FA2_TOKEN_UNDEFINED"; FAILWITH; }; DUP; GET 4; PUSH nat 0; COMPARE; LT; IF { DUP; GET 4; DUP 5; CAR; GET 3; DUP 4; CAR; GET; IF_SOME { } { PUSH int 408; FAILWITH; }; COMPARE; GE; IF {} { PUSH string "FA2_INSUFFICIENT_BALANCE"; FAILWITH; }; DUP 4; UNPAIR; UNPAIR; SWAP; UNPAIR; DUP; DUP 7; CAR; DUP; DUG 2; GET; IF_SOME { DROP; } { PUSH int 412; FAILWITH; }; DUP 6; GET 4; DIG 9; CAR; GET 3; DUP 9; CAR; GET; IF_SOME { } { PUSH int 412; FAILWITH; }; SUB; ISNAT; IF_SOME {} { PUSH int 412; FAILWITH; }; SOME; SWAP; UPDATE; PAIR; SWAP; PAIR; PAIR; DUP; DUG 4; CAR; GET 3; SWAP; DUP; DUG 2; CAR; MEM; IF { DIG 3; UNPAIR; UNPAIR; SWAP; UNPAIR; DUP; DUP 6; CAR; DUP; DUG 2; GET; IF_SOME {} { PUSH int 415; FAILWITH; }; DIG 6; GET 4; ADD; SOME; SWAP; UPDATE; PAIR; SWAP; PAIR; PAIR; DUG 2; } { DIG 3; UNPAIR; UNPAIR; SWAP; UNPAIR; DUP 5; GET 4; SOME; DIG 5; CAR; UPDATE; PAIR; SWAP; PAIR; PAIR; DUG 2; }; } { DROP; }; }; DROP; }; DROP; } { DUP; ITER { IF_LEFT { DUP; CAR; SENDER; COMPARE; EQ; IF { PUSH bool True; } { DUP 3; CAR; CAR; CAR; SENDER; COMPARE; EQ; }; IF {} { PUSH string "FA2_NOT_ADMIN_OR_OPERATOR"; FAILWITH; }; DIG 2; UNPAIR; SWAP; UNPAIR; UNPAIR; PUSH (option unit) (Some Unit); DIG 5; DUP; GET 4; SWAP; DUP; GET 3; SWAP; CAR; PAIR 3; UPDATE; PAIR; PAIR; SWAP; PAIR; SWAP; } { DUP; CAR; SENDER; COMPARE; EQ; IF { PUSH bool True; } { DUP 3; CAR; CAR; CAR; SENDER; COMPARE; EQ; }; IF {} { PUSH string "FA2_NOT_ADMIN_OR_OPERATOR"; FAILWITH; }; DIG 2; UNPAIR; SWAP; UNPAIR; UNPAIR; NONE unit; DIG 5; DUP; GET 4; SWAP; DUP; GET 3; SWAP; CAR; PAIR 3; UPDATE; PAIR; PAIR; SWAP; PAIR; SWAP; }; }; DROP; }; }; NIL operation; }; PAIR; };` ; const paused = pause ? "True" : "False" ; const storage = `(Pair (Pair (Pair "${ administrator }" 0) (Pair {} {Elt ""  0x${ Buffer . from ( metadataUrl , "utf-8" ) . toString ( "hex" ) }})) (Pair (Pair {} ${ paused }) (Pair {} {})))` ; const nodeResult = await TezosNodeWriter . sendContractOriginationOperation ( server , signer , keystore , 0 , undefined , fee , freight , gas , contract , storage , TezosTypes . TezosParameterFormat . Michelson ) ; return TezosContractUtils . clearRPCOperationGroupHash ( nodeResult [ 'operationGroupID' ] ) ; } param server param address export async function getSimpleStorage ( server , address ) { const storageResult = await TezosNodeReader . getContractStorage ( server , address ) ; return { administrator : JSONPath ( { path : '$.args[0].args[0].args[0].string' , json : storageResult } ) [ 0 ] , tokens : Number ( JSONPath ( { path : '$.args[0].args[0].args[1].int' , json : storageResult } ) [ 0 ] ) , ledger : Number ( JSONPath ( { path : '$.args[0].args[1].int' , json : storageResult } ) [ 0 ] ) , metadata : Number ( JSONPath ( { path : '$.args[0].args[2].int' , json : storageResult } ) [ 0 ] ) , paused : ( JSONPath ( { path : '$.args[1].args[1].prim' , json : storageResult } ) [ 0 ] ) . toString ( ) . toLowerCase ( ) . startsWith ( 't' ) , operators : Number ( JSONPath ( { path : '$.args[1].args[0].int' , json : storageResult } ) [ 0 ] ) , tokenMetadata : Number ( JSONPath ( { path : '$.args[2].int' , json : storageResult } ) [ 0 ] ) , totalSupply : Number ( JSONPath ( { path : '$.args[3].int' , json : storageResult } ) [ 0 ] ) , } ; } param server param mapid export async function getTokenDefinition ( server , mapid ) { const packedKey = TezosMessageUtils . encodeBigMapKey ( Buffer . from ( TezosMessageUtils . writePackedData ( 0 , 'nat' ) , 'hex' ) ) ; const mapResult = await TezosNodeReader . getValueForBigMapKey ( server , mapid , packedKey ) ; if ( mapResult === undefined ) { throw new Error ( `Map ${ mapid } does not contain a record for token 0` ) ; } const tokenData = { tokenid : Number ( JSONPath ( { path : '$.args[0].int' , json : mapResult } ) [ 0 ] ) , metadata : { } } ; mapResult [ "args" ] [ 1 ] . forEach ( item => { tokenData . metadata [ item [ "args" ] [ 0 ] [ "string" ] ] = Buffer . from ( item [ "args" ] [ 1 ] [ "bytes" ] , "hex" ) . toString ( ) ; } ) ; return tokenData ; } param server param address param signer param keystore param fee param gas param freight export async function activate ( server , address , signer , keystore , fee , gas = 800000 , freight = 20000 ) { const entryPoint = 'set_pause' ; const parameters = 'False' ; const nodeResult = await TezosNodeWriter . sendContractInvocationOperation ( server , signer , keystore , address , 0 , fee , freight , gas , entryPoint , parameters , TezosTypes . TezosParameterFormat . Michelson ) ; return TezosContractUtils . clearRPCOperationGroupHash ( nodeResult . operationGroupID ) ; } param server param address param signer param keystore param fee param gas param freight export async function deactivate ( server , address , signer , keystore , fee , gas = 800000 , freight = 20000 ) { const entryPoint = 'set_pause' ; const parameters = 'True' ; const nodeResult = await TezosNodeWriter . sendContractInvocationOperation ( server , signer , keystore , address , 0 , fee , freight , gas , entryPoint , parameters , TezosTypes . TezosParameterFormat . Michelson ) ; return TezosContractUtils . clearRPCOperationGroupHash ( nodeResult . operationGroupID ) ; } export async function changeAdministrator ( server , address , signer , keystore , fee , administrator , gas = 800000 , freight = 20000 ) { const entryPoint = 'set_administrator' ; const parameters = `"${ administrator }"` ; const nodeResult = await TezosNodeWriter . sendContractInvocationOperation ( server , signer , keystore , address , 0 , fee , freight , gas , entryPoint , parameters , TezosTypes . TezosParameterFormat . Michelson ) ; return TezosContractUtils . clearRPCOperationGroupHash ( nodeResult . operationGroupID ) ; } param server param address param signer param keystore param fee param destination param amount param metadata param gas param freight returns export async function mint ( server , address , signer , keystore , fee , destination , amount , metadata , gas = 800000 , freight = 20000 ) { const entryPoint = 'mint' ; const keys = Object . keys ( metadata ) . sort ( ) ; let metaString = "" ; for ( let key of keys ) { if ( metaString !== "" ) metaString += "; " ; metaString += `Elt "${ key }" 0x${ Buffer . from ( metadata [ key ] , "utf-8" ) . toString ( "hex" ) }` ; } const parameters = `(Pair (Pair "${ destination }" ${ amount }) (Pair {${ metaString }} 1))` ; const nodeResult = await TezosNodeWriter . sendContractInvocationOperation ( server , signer , keystore , address , 0 , fee , freight , gas , entryPoint , parameters , TezosTypes . TezosParameterFormat . Michelson ) ; return TezosContractUtils . clearRPCOperationGroupHash ( nodeResult . operationGroupID ) ; } export async function transfer ( server , address , signer , keystore , fee , source , transfers , gas = 800000 , freight = 20000 ) { const entryPoint = 'transfer' ; const parameters = `{ Pair 0x${ TezosMessageUtils . writeAddress ( source ) } { ${ transfers . map ( t => '( Pair 0x' + TezosMessageUtils . writeAddress ( t . address ) + ' ( Pair ' + 0 + ' ' + t . amount + ' ) )' ) . join ( ' ; ' ) } } }` ; const nodeResult = await TezosNodeWriter . sendContractInvocationOperation ( server , signer , keystore , address , 0 , fee , freight , gas , entryPoint , parameters , TezosTypes . TezosParameterFormat . Michelson , TezosConstants . HeadBranchOffset , true ) ; return TezosContractUtils . clearRPCOperationGroupHash ( nodeResult . operationGroupID ) ; } export async function addOperators ( server , address , signer , keystore , fee , updateOps , gas = 800000 , freight = 20000 ) { const entryPoint = 'update_operators' ; let parameters = "{" ; updateOps . forEach ( op => { if ( parameters !== "{" ) parameters += "; " ; parameters += `Left (Pair "${ op . owner }" (Pair "${ op . operator }" 0))` ; } ) ; parameters += "}" ; const nodeResult = await TezosNodeWriter . sendContractInvocationOperation ( server , signer , keystore , address , 0 , fee , freight , gas , entryPoint , parameters , TezosTypes . TezosParameterFormat . Michelson ) ; return TezosContractUtils . clearRPCOperationGroupHash ( nodeResult . operationGroupID ) ; } export async function removeOperators ( server , address , signer , keystore , fee , updateOps , gas = 800000 , freight = 20000 ) { const entryPoint = 'update_operators' ; let parameters = "{" ; updateOps . forEach ( op => { if ( parameters !== "{" ) parameters += "; " ; parameters += `Right (Pair "${ op . owner }" (Pair "${ op . operator }" 0))` ; } ) ; parameters += "}" ; const nodeResult = await TezosNodeWriter . sendContractInvocationOperation ( server , signer , keystore , address , 0 , fee , freight , gas , entryPoint , parameters , TezosTypes . TezosParameterFormat . Michelson ) ; return TezosContractUtils . clearRPCOperationGroupHash ( nodeResult . operationGroupID ) ; } export async function getAccountBalance ( server , mapid , account , balancePath = '$.int' ) { const packedKey = TezosMessageUtils . encodeBigMapKey ( Buffer . from ( TezosMessageUtils . writePackedData ( account , "address" ) , 'hex' ) ) ; const mapResult = await TezosNodeReader . getValueForBigMapKey ( server , mapid , packedKey ) ; if ( mapResult === undefined ) { throw new Error ( `Map ${ mapid } does not contain a record for ${ account }` ) ; } const balance = Number ( JSONPath ( { path : balancePath , json : mapResult } ) [ 0 ] ) ; return balance ; } }