Exemple #1
0
def Main(operation, ctr):

    if operation == 'get_contract':
        return GetContract(ctr)

    elif operation == 'get_script':
        return GetContract(ctr).Script

    elif operation == 'get_storage_context':
        return GetContract(ctr).StorageContext

    elif operation == 'destroy':
        Destroy()
        return True

    return 'unknown operation'
Exemple #2
0
def CheckWitnessOrCaller(scripthash, caller):
    """ 
    Method to check if the transaction is signed by a private key
    or is the scripthash of a contract that is authorized to perform
    the requested function for its own address only

    :param scripthash: the scripthash to be checked
    :type scripthash: bytearray
    :param caller: the scripthash of the calling script
    :type caller: bytearray

    :return: whether the scripthash is authorized
    :rtype: bool

    """

    if GetContract(scripthash):
        if scripthash == caller:
            return True  # a contract can spend its own funds
        else:
            # deny third-party contracts from transferring
            # tokens of a user even with the user signature
            # (this will break ability of some DEX to list the token)
            return False

    return CheckWitness(scripthash)
Exemple #3
0
def do_mint_token(ctx, args):
    """Mints a new NFT token; stores it's properties, URI info, and
    owner on the blockchain; updates the totalSupply

    :param StorageContext ctx: current store context
    :param list args:
        0: byte[] t_owner: token owner
        1: byte[] t_properties: token's read only data
        2: bytes t_uri: token's uri
        3: extra_arg (optional): extra arg to be passed to a smart
            contract
    :return: mint success
    :rtype: bool
    """
    t_id = Get(ctx, TOKEN_CIRC_KEY)
    # the int 0 is represented as b'' in neo-boa, this caused bugs
    # throughout my code
    # This is the reason why token id's start at 1 instead
    t_id += 1

    # this should never already exist
    if len(Get(ctx, t_id)) == 20:
        Notify('token already exists')
        return False

    t_owner = args[0]
    if len(t_owner) != 20:
        Notify(INVALID_ADDRESS_ERROR)
        return False

    t_properties = args[1]
    if len(t_properties) == b'\x00':
        Notify('missing properties data string')
        return False

    t_uri = args[2]

    if GetContract(t_owner):
        contract_args = [t_owner, t_id]
        if len(args) == 4:  # append optional extra arg
            contract_args.append(args[3])

        success = transfer_to_smart_contract(ctx, GetExecutingScriptHash(),
                                             contract_args, True)
        if success is False:
            return False

    Put(ctx, t_id, t_owner)  # update token's owner
    Put(ctx, concat('properties/', t_id), t_properties)
    Put(ctx, concat('uri/', t_id), t_uri)
    add_token_to_owners_list(ctx, t_owner, t_id)
    Put(ctx, TOKEN_CIRC_KEY, t_id)  # update total supply

    # Log this minting event
    OnMint(t_owner, 1)
    OnNFTMint(t_owner, t_id)
    return True
Exemple #4
0
def do_approve(ctx, caller, t_receiver, t_id, revoke):
    """Approve a token to eventually be transferred to the t_receiver

    :param StorageContext ctx: current store context
    :param byte[] caller: calling script hash
    :param byte[] t_receiver: address of the future token owner
    :param bytes t_id: int: token id
    :param bytes revoke: set to 1 to revoke previous approval
    :return: approval success
    :rtype: bool
    """
    if len(t_receiver) != 20:
        Notify(INVALID_ADDRESS_ERROR)
        return False

    if len(revoke) == b'\x00':
        revoke = b'\x00'

    t_owner = Get(ctx, t_id)
    if len(t_owner) != 20:
        Notify(TOKEN_DNE_ERROR)
        return False

    if t_owner == t_receiver:
        Notify('approved spend to self')
        return True

    is_token_owner = CheckWitness(t_owner)
    if is_token_owner and GetEntryScriptHash() != caller:
        Notify('third party script is bouncing the signature to us')
        return False
    # if token owner is a smart contract and is the calling
    # script hash, continue
    elif GetContract(t_owner) and t_owner == caller:
        is_token_owner = True

    if is_token_owner:
        approval_key = concat('approved/', t_id)
        # revoke previous approval if revoke != 0
        if revoke != b'\x00':
            Delete(ctx, approval_key)
            # log the revoking of previous approvals
            OnApprove(t_owner, t_receiver, b'\x00')
            OnNFTApprove(t_owner, '', t_id)
            return True

        # approve this transfer
        Put(ctx, approval_key, concat(t_owner, t_receiver))

        # Log this approval event
        OnApprove(t_owner, t_receiver, 1)
        OnNFTApprove(t_owner, t_receiver, t_id)
        return True

    Notify(PERMISSION_ERROR)
    return False
Exemple #5
0
def do_transfer_from(ctx, args):
    """Transfers the approved token at the specified id from the
    t_from address to the t_to address

    :param StorageContext ctx: current store context
    :param list args:
        0: byte[] t_from: transfer from address (token owner)
        1: byte[] t_to: transfer to address (token receiver)
        2: bytes t_id: token id
        3: extra_arg: optional argument that can be passed (for use
            only with smart contracts)
    :return: transferFrom success
    :rtype: bool
    """
    t_from = args[0]
    t_to = args[1]
    t_id = args[2]

    if len(t_from) != 20 or len(t_to) != 20:
        Notify(INVALID_ADDRESS_ERROR)
        return False

    if t_from == t_to:
        Notify('transfer to self')
        return True

    t_owner = Get(ctx, t_id)
    if len(t_owner) != 20:
        Notify(TOKEN_DNE_ERROR)
        return False

    if t_from != t_owner:
        Notify('from address is not the owner of this token')
        return False

    approval_key = concat('approved/', t_id)
    # authorized spend should be concat(t_owner, t_receiver)
    authorized_spend = Get(ctx, approval_key)

    # len(t_owner) == 20 and len(t_receiver) == 20, thus the length of
    # authorized_spender should be 40
    if len(authorized_spend) != 40:
        Notify('no approval exists for this token')
        return False

    # if the input transfer from and transfer to addresses match the
    # authorized spend
    if authorized_spend == concat(t_from, t_to):
        # 1. Is t_to a smart contract?
        # If True, invoke the transfer_to_smart_contract method.
        # if transfer_to_smart_contract() returns False, then
        # reject the transfer
        if GetContract(t_to):
            args.remove(0)
            success = transfer_to_smart_contract(ctx, t_from, args, False)
            if success is False:
                return False
        else:
            # if t_to is not a contract, there shouldn't be any
            # extra args to transfer(), this could be a phishing
            # attempt so reject the transfer
            if len(args) > 3:
                Notify(ARG_ERROR)
                return False

        res = remove_token_from_owners_list(ctx, t_from, t_id)
        if res is False:
            Notify('unable to transfer token')
            return False

        Put(ctx, t_id, t_to)  # record token's new owner
        Delete(ctx, approval_key)  # remove previous approval
        add_token_to_owners_list(ctx, t_to, t_id)

        # log this transfer event
        OnTransfer(t_from, t_to, 1)
        OnNFTTransfer(t_from, t_to, t_id)
        return True

    Notify(PERMISSION_ERROR)
    return False
Exemple #6
0
def do_transfer(ctx, caller, args):
    """Transfers a token at the specified id from the t_owner address
    to the t_to address

    :param StorageContext ctx: current store context
    :param bytes caller: calling script hash
    :param list args:
        0: byte[] t_to: transfer to address
        1: bytes t_id: token id
        2: extra_arg: optional argument that can be passed (for use
            only with smart contracts)
    :return: transfer success
    :rtype: bool
    """
    t_to = args[0]
    t_id = args[1]

    if len(t_to) != 20:
        Notify(INVALID_ADDRESS_ERROR)
        return False

    t_owner = Get(ctx, t_id)
    if len(t_owner) != 20:
        Notify(TOKEN_DNE_ERROR)
        return False

    if t_owner == t_to:
        Notify('transfer to self')
        return True

    # Verifies that the calling contract has verified the required
    # script hashes of the transaction/block
    is_token_owner = CheckWitness(t_owner)
    if is_token_owner and GetEntryScriptHash() != caller:
        Notify('third party script is bouncing the signature to us')
        return False
    # if token owner is a smart contract and is the calling
    # script hash, continue
    elif GetContract(t_owner) and t_owner == caller:
        is_token_owner = True

    if is_token_owner:
        # 1. Is t_to a smart contract?
        # If True, invoke the transfer_to_smart_contract
        # method, if transfer_to_smart_contract() returns False,
        # then reject the transfer
        if GetContract(t_to):
            success = transfer_to_smart_contract(ctx, t_owner, args, False)
            if success is False:
                return False
        else:
            if len(args) > 2:
                Notify(ARG_ERROR)
                return False

        res = remove_token_from_owners_list(ctx, t_owner, t_id)
        if res is False:
            Notify('unable to transfer token')
            return False

        Put(ctx, t_id, t_to)  # update token's owner
        # remove any existing approvals for this token
        Delete(ctx, concat('approved/', t_id))
        add_token_to_owners_list(ctx, t_to, t_id)

        # log this transfer event
        OnTransfer(t_owner, t_to, 1)
        OnNFTTransfer(t_owner, t_to, t_id)
        return True

    Notify(PERMISSION_ERROR)
    return False
Exemple #7
0
def authenticate(scripthash, Caller):
    if CheckWitness(scripthash): return True
    if GetContract(scripthash) and scripthash == Caller: return True
    return False
Exemple #8
0
def Main(operation, args):
    """Entry point to the program

    :param str operation: The name of the operation to perform
    :param list args: A list of arguments along with the operation
    :return: The result of the operation
    :rtype: bytearray

    Token operations:
    - allowance(token_id): returns approved third-party spender of a
        token
    - approve(token_receiver, token_id, revoke): approve third party
        to spend a token
    - balanceOf(owner): returns owner's current total tokens owned
    - name(): returns name of token
    - ownerOf(token_id): returns the owner of the specified token.
    - postMintContract(): returns the contract that a freshly minted
        token gets sent to by default
    - properties(token_id): returns a token's read-only data
    - supportedStandards(): returns a list of supported standards
        {"NEP-10"}
    - symbol(): returns token symbol
    - tokensOfOwner(owner, starting_index): returns a list that
        contains less than or equal to ten of the tokens owned by
        the specified address starting at the `starting_index`.
    - totalSupply(): Returns the total token supply deployed in the
        system.
    - transfer(to, token_id): transfers a token
    - transferFrom(from, to, token_id): allows a third party to
        execute an approved transfer
    - uri(token_id): Returns a distinct Uniform Resource Identifier
        (URI) for a given asset.
        The URI data of a token supplies a reference to get more
        information about a specific token or its data.

    TOKEN_CONTRACT_OWNER operations:
        - mintToken(properties, URI, owner, extra_arg): create a new
            NFT token with the specified properties and URI
        - modifyURI(token_id, token_data): modify specified token's
            URI data

        setters:
        - setName(name): sets the name of the token
        - setPostMintContract(contract_address): sets the contract
            freshly minted tokens get sent to by default
        - setSymbol(symbol): sets the token's symbol
        - setSupportedStandards(supported_standards): sets the
            supported standards, 'NEP-10' must be the first element
            in the array
    """
    # The trigger determines whether this smart contract is being run
    # in 'verification' mode or 'application'
    trigger = GetTrigger()

    # 'Verification' mode is used when trying to spend assets
    # (eg NEO, Gas) on behalf of this contract's address
    if trigger == Verification():

        # if the script that sent this is the owner, we allow the spend
        if CheckWitness(TOKEN_CONTRACT_OWNER):
            return True

    elif trigger == Application():

        ctx = GetContext()

        if operation == 'name':
            name = Get(ctx, 'name')
            if name:
                return name
            else:
                return TOKEN_NAME

        elif operation == 'symbol':
            symbol = Get(ctx, 'symbol')
            if symbol:
                return symbol
            else:
                return TOKEN_SYMBOL

        elif operation == 'supportedStandards':
            supported_standards = Get(ctx, 'supportedStandards')
            if supported_standards:
                return supported_standards
            else:
                return Serialize(['NEP-10'])

        elif operation == 'postMintContract':
            return Get(ctx, 'postMintContract')

        elif operation == 'totalSupply':
            return Get(ctx, TOKEN_CIRC_KEY)

        if operation == 'allowance':
            if len(args) == 1:
                return Get(ctx, concat('approved/', args[0]))

            Notify(ARG_ERROR)
            return False

        elif operation == 'approve':
            if len(args) == 3:
                # GetCallingScriptHash() can't be done within the
                # function because the calling script hash changes
                # depending on where the function is called
                return do_approve(ctx, GetCallingScriptHash(), args[0],
                                  args[1], args[2])

            Notify(ARG_ERROR)
            return False

        elif operation == 'balanceOf':
            if len(args) == 1:
                if len(args[0]) == 20:
                    return Get(ctx, args[0])

                Notify(INVALID_ADDRESS_ERROR)
                return False

            Notify(ARG_ERROR)
            return False

        elif operation == 'ownerOf':
            if len(args) == 1:
                t_owner = Get(ctx, args[0])
                if len(t_owner) == 20:
                    return t_owner

                Notify(TOKEN_DNE_ERROR)
                return False

            Notify(ARG_ERROR)
            return False

        elif operation == 'properties':
            if len(args) == 1:
                token_properties = Get(ctx, concat('properties/', args[0]))
                if token_properties:
                    return token_properties

                Notify(TOKEN_DNE_ERROR)
                return False

            Notify(ARG_ERROR)
            return False

        elif operation == 'transfer':
            if len(args) >= 2:
                # GetCallingScriptHash() can't be done within the
                # function because the calling script hash changes
                # depending on where the function is called
                return do_transfer(ctx, GetCallingScriptHash(), args)

            Notify(ARG_ERROR)
            return False

        elif operation == 'transferFrom':
            if len(args) >= 3:
                return do_transfer_from(ctx, args)

            Notify(ARG_ERROR)
            return False

        elif operation == 'tokensOfOwner':
            if len(args) == 2:
                return do_tokens_of_owner(ctx, args[0], args[1])

            Notify(ARG_ERROR)
            return False

        elif operation == 'uri':
            if len(args) == 1:
                token_uri = Get(ctx, concat('uri/', args[0]))
                if token_uri:
                    return token_uri

                Notify(TOKEN_DNE_ERROR)
                return False

            Notify(ARG_ERROR)
            return False

        # Administrative operations
        if CheckWitness(TOKEN_CONTRACT_OWNER):
            if operation == 'mintToken':
                if len(args) >= 2:
                    return do_mint_token(ctx, args)

                Notify(ARG_ERROR)
                return False

            elif operation == 'modifyURI':
                if len(args) == 2:
                    return do_modify_uri(ctx, args[0], args[1])

                Notify(ARG_ERROR)
                return False

            elif operation == 'setName':
                if len(args) == 1:
                    return do_set_config(ctx, 'name', args[0])

                Notify(ARG_ERROR)
                return False

            elif operation == 'setSymbol':
                if len(args) == 1:
                    return do_set_config(ctx, 'symbol', args[0])

                Notify(ARG_ERROR)
                return False

            elif operation == 'setPostMintContract':
                if len(args) == 1:
                    if len(args[0]) == 20:
                        if GetContract(args[0]):
                            return do_set_config(ctx, 'postMintContract',
                                                 args[0])

                        Notify('address is not a contract')
                        return False

                    Notify(INVALID_ADDRESS_ERROR)
                    return False

                Notify(ARG_ERROR)
                return False

            elif operation == 'setSupportedStandards':
                if len(args) >= 1:
                    if args[0] != 'NEP-10':
                        Notify('NEP-10 must be the first arg')
                        return False

                    return do_set_config(ctx, 'supportedStandards',
                                         Serialize(args))

                Notify(ARG_ERROR)
                return False

        else:
            Notify(PERMISSION_ERROR)
            return False

        Notify('unknown operation')
    return False
Exemple #9
0
def do_mint_token(ctx, args):
    """Mints a new NFT token; stores it's properties, URI info, and
    owner on the blockchain; updates the totalSupply

    :param StorageContext ctx: current store context
    :param list args:
        0: byte[] t_properties: token's read only data
        1: bytes t_uri: token's uri
        2: byte[] t_owner (optional): default is postMintContract,
            can be a user address, or another smart contract
        3: extra_arg (optional): extra arg to be passed to smart
            contract
    :return: new total supply of tokens
    :rtype: boolean or integer
    """
    t_id = Get(ctx, TOKEN_CIRC_KEY)
    # the int 0 is represented as b'' in neo-boa, this caused bugs
    # throughout my code
    # This is the reason why token id's start at 1 instead
    t_id += 1

    exists = Get(ctx, t_id)  # this should never already exist
    if len(exists) == 20:
        Notify('token already exists')
        return False

    t_properties = args[0]
    if len(t_properties) == b'\x00':
        Notify('missing properties data string')
        return False

    t_uri = args[1]

    # if nft contract owner passed a third argument,
    # check if it is a user/contract address, if so set t_owner
    # to the specified address
    t_owner = b''
    if len(args) > 2:
        if len(args[2]) == 20:
            t_owner = args[2]

    # if nft contract owner didn't pass an address, transfer the
    # newly minted token to the default contract.
    # If nft contract owner did pass an address and it is a
    # smart contract, transfer the newly minted token to the
    # passed contract
    this_contract = GetExecutingScriptHash()
    if len(t_owner) != 20:
        t_owner = Get(ctx, 'postMintContract')
        contract_args = [t_owner, t_id]
        if len(args) == 3:  # append optional extra_arg
            contract_args.append(args[2])

        success = transfer_to_smart_contract(ctx, this_contract, contract_args,
                                             True)
        if success is False:
            return False
    elif len(t_owner) == 20:
        if GetContract(t_owner):
            contract_args = [t_owner, t_id]
            if len(args) == 4:  # append optional extra arg
                contract_args.append(args[3])

            success = transfer_to_smart_contract(ctx, this_contract,
                                                 contract_args, True)
            if success is False:
                return False

    Put(ctx, t_id, t_owner)  # update token's owner
    Put(ctx, concat('properties/', t_id), t_properties)
    Put(ctx, concat('uri/', t_id), t_uri)
    add_token_to_owners_list(ctx, t_owner, t_id)
    Put(ctx, TOKEN_CIRC_KEY, t_id)  # update total supply

    # Log this minting event
    OnMint(t_owner, 1)
    OnNFTMint(t_owner, t_id)
    return t_id
def mint_nft_token(ctx, args):
    """Mints a new NFT token; stores it's properties, URI info, and
    owner on the blockchain; updates the totalSupply

    :param StorageContext ctx: current store context
    :param list args:
        0: byte[] t_owner: token owner
        1: byte[] t_properties: manufacture_data_json
        2: extra_arg (optional): extra arg to be passed to a smart
            contract
    :return: mint success
    :rtype: bool

    build partchain.py test 0710 05 True False False mint_nft_token ["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y", '{"Manufacture_name": "ROLLSROYCE", "Part_name": "Radar", "Serial_number":"1234567890"}']
    """

    details = json.loads(args[1])

    t_id = Get(ctx, TOKEN_CIRC_KEY)
    # the int 0 is represented as b'' in neo-boa, this caused bugs
    # throughout my code
    # This is the reason why token id's start at 1 instead
    t_id += 1

    # this should never already exist
    if len(Get(ctx, t_id)) == 20:
        Notify('token already exists')
        return False

    t_owner = args[0]
    if len(t_owner) != 20:
        Notify(INVALID_ADDRESS_ERROR)
        return False

    t_properties = args[1]
    if len(t_properties) == b'\x00':
        Notify('missing properties data string')
        return False

    t_uri = details['Manufacture_name'] + "_" + details[
        'Part_name'] + "_" + details['Serial_number']
    print(t_uri)

    if GetContract(t_owner):
        contract_args = [t_owner, t_id]
        if len(args) == 3:  # append optional extra arg
            contract_args.append(args[2])

        success = transfer_to_smart_contract(ctx, GetExecutingScriptHash(),
                                             contract_args, True)
        if success is False:
            return False

    Put(ctx, t_id, t_owner)  # update token's owner
    Put(ctx, concat('properties/', t_id), t_properties)
    Put(ctx, concat('uri/', t_id), t_uri)
    add_token_to_owners_list(ctx, t_owner, t_id)
    Put(ctx, TOKEN_CIRC_KEY, t_id)  # update total supply

    # Log this minting event
    OnMint(t_owner, 1)
    OnNFTMint(t_owner, t_id)
    return True