Skip to content

Sample Code for Exchange

TW edited this page Feb 19, 2021 · 3 revisions

Chain related statement

We could create chain object like this:

# (TestNet)
import pyvsystems as pv

chain = pv.testnet_chain()

or

# (MainNet)
import pyvsystems as pv

m_wrapper = pv.create_api_wrapper('http://<full node ip>:9922')
chain = pv.Chain(chain_name='mainnet', chain_id='M', address_version=5, api_wrapper= m_wrapper)

Get transactions at block height

import pyvsystems as pv

chain = pv.testnet_chain()
print(chain.block(7937452))

Sample result:

{
  'version': 1,
  'timestamp': 1572342178010809171,
  'reference': '4uaHS9eHGxxCEbBjmnfSmA6jFj1twJwHHbThocsp96dxhPWaEYi69rzm45fsghpidtpyTJQbKxiB9g7jSSe1Xcsu',
  'SPOSConsensus': {
    'mintTime': 1572342178000000000,
    'mintBalance': 350858193805008
  },
  'resourcePricingData': {
    'computation': 0,
    'storage': 0,
    'memory': 0,
    'randomIO': 0,
    'sequentialIO': 0
  },
  'TransactionMerkleRoot': 'CNMWF7N9HQUj22qW4oTDw8gpsuNh5ttaSuxctNf3nbtc',
  'transactions': [
    {
      'type': 2,
      'id': 'GwzuMwzn1iFBc6CogaxuNcxiiZXaaGeCSk5MCyCFj4Gi',
      'fee': 10000000,
      'feeScale': 100,
      'timestamp': 1572342176723299072,
      'proofs': [
        {
          'proofType': 'Curve25519',
          'publicKey': '8PtidoeohqEyAyuqdBGudWfr7i1mUVrhNkQHNRfvaXvq',
          'address': 'AU1vwC7C6CqV2q4oZYFNTbBJYNaakMBQyZV',
          'signature': '67CUqt47cFWh38osURxn6wvLYW5hdmkPoVrddYH5HhRS2SDyDzVzihpffJEBqLEQwKmQeA7VJ5iWazBAmWGjcevS'
        }
      ],
      'recipient': 'AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8',
      'amount': 100000000,
      'attachment': '',
      'status': 'Success',
      'feeCharged': 10000000
    },
    {
      'type': 5,
      'id': 'Da2dRCXx6jUeV1ce1wVeEEAZhUUT6H89ywvf4vvThtpn',
      'recipient': 'AUDRgBJjXM5zFMERzMML7pLPWikajTf8AKh',
      'timestamp': 1572342178010809171,
      'amount': 900000000,
      'currentBlockHeight': 7937452,
      'status': 'Success',
      'feeCharged': 0
    }
  ],
  'generator': 'AUDRgBJjXM5zFMERzMML7pLPWikajTf8AKh',
  'signature': 'SVeakw17ruk3v2DVRTcWYv35UHAE94R5BKBGHdBdR1rWiSNWMCgoDV4nuAfQMeoiDhaKyiWaYEVdq6fTiY6S7L4',
  'fee': 10000000,
  'blocksize': 500,
  'height': 7937452,
  'transaction count': 2
}

Account Management

  • Create new account with random seed
import pyvsystems as pv
from pyvsystems import Account

my_account = Account(chain=pv.testnet_chain())
print(my_account.get_info())  # show account info
print(my_account.privateKey)  # show account private key

The account data is only stored in memory. It would be better to backup the private key into your database.

  • Restore account by private key
import pyvsystems as pv
from pyvsystems import Account

pk = "28XHDwLzKaXRLspTBuxqurrah6apEfbJGh6Nnt914u2T"
restore_account = Account(chain=pv.testnet_chain(), private_key=pk)

Get Balance of V Coin and Token

  • Get account V Coin balance
import pyvsystems as pv
from pyvsystems import Account

address = 'AU4mSZPtUwDD9sD3bT1TVBkgcZiq3SgE1n1'
my_account = Account(chain=pv.testnet_chain(), address=address)
print(my_account.get_info()) 

The detail explain of reponse JSON is:

{
	'regular': 0, 		# regular balance
	'available': 0,  	# available balance (regular - leased out)
	'effective': 0,  	# effective balance (regular - leases out + leased in)
	'mintingAverage': 0,  # for minter used
	'height': 643936, 
	'publicKey': '8PtidoeohqEyAyuqdBGudWfr7i1mUVrhNkQHNRfvaXvq', 
	'address': 'AU1vwC7C6CqV2q4oZYFNTbBJYNaakMBQyZV'
}

Among of these balances, available is final balance which user could be used.

  • Get Token info and account Token balance
import pyvsystems as pv
from pyvsystems import Account
from pyvsystems.contract import token_id_from_contract_id

custom_wrapper = pv.create_api_wrapper('https://test.v.systems/api')
chain = pv.Chain(chain_name='testnet', chain_id='T', address_version=5, api_wrapper=custom_wrapper)

account = Account(chain=chain, address='AU4mSZPtUwDD9sD3bT1TVBkgcZiq3SgE1n1')

contract_id = 'CFEBiQG3WtJNWFhtEj1CDDg6Wy7G6P7BPSy'
token_id = token_id_from_contract_id(contract_id, 0)
print("token_id: ", token_id)
print("token balance: ", account.token_balance(token_id))

Sample result:

token_id:  TWurhe6uRbuUw566dKm89Jrc9whSkUMHSjNYtFEJn
token balance:  97675600000000

Send V Coin (Withdraw V Coin)

For example, send V Coins to address "AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8" (The default transaction fee is 0.1 VSYS, which is minimum transaction fee. fee_scale should be 100.)

import pyvsystems as pv
from pyvsystems import Account

if __name__ == "__main__":
    pk = "28XHDwLzKaXRLspTBuxqurrah6apEfbJGh6Nnt914u2T"
    my_account = Account(chain=pv.testnet_chain(), private_key=pk)
    print(my_account.get_info())
    recipient_address = "AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8"
    recipient = Account(chain=chain, address=recipient_address)
    # send payment (100000000 = 1 VSYS)
    amount = 100000000

    # For hot wallet sending transaction
    resp = my_account.send_payment(recipient, amount=amount)
    print(resp)

    # For cold wallet signing transaction
    pv.set_offline()
    resp = my_account.send_payment(recipient, amount=amount)
    print(resp)

Sample result:

{
  'type': 2,
  'id': 'GwzuMwzn1iFBc6CogaxuNcxiiZXaaGeCSk5MCyCFj4Gi',
  'fee': 10000000,
  'feeScale': 100,
  'timestamp': 1572342176723299072,
  'proofs': [
    {
      'proofType': 'Curve25519',
      'publicKey': '8PtidoeohqEyAyuqdBGudWfr7i1mUVrhNkQHNRfvaXvq',
      'address': 'AU1vwC7C6CqV2q4oZYFNTbBJYNaakMBQyZV',
      'signature': '67CUqt47cFWh38osURxn6wvLYW5hdmkPoVrddYH5HhRS2SDyDzVzihpffJEBqLEQwKmQeA7VJ5iWazBAmWGjcevS'
    }
  ],
  'recipient': 'AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8',
  'amount': 100000000,
  'attachment': ''
}

Send Token (Withdraw Token)

import pyvsystems as pv
from pyvsystems import Account
from pyvsystems.contract_helper import *

token_without_split_helper = TokenWithoutSplitContractHelper()
token_with_split_helper = TokenWithSplitContractHelper()

SEND_NORMAL_TYPE_TOKEN_FUNC_INDEX = token_without_split_helper.send_function_index
SEND_SPLIT_TYPE_TOKEN_FUNC_INDEX = token_with_split_helper.send_function_index

# Known token info should store in DB
known_token_info = {
    "CFEBiQG3WtJNWFhtEj1CDDg6Wy7G6P7BPSy": {
        "token_id": "TWurhe6uRbuUw566dKm89Jrc9whSkUMHSjNYtFEJn",
        "token_name": "Token A",
        "contract_type": "TokenContract",
        "unity": 100000000
    },
    "CF89wmAjkThqK4KzxSBYjeWe2mKaF61vzfF": {
        "token_id": "TWuBEsV7iRc7qzZbeM2BWU4R5PhTWVBBbc6wdfQBs",
        "token_name": "Token B",
        "contract_type": "TokenContractWithSplit",
        "unity": 1000000
    }
}


def get_send_function_index(contract_id):
    if known_token_info.get(contract_id):
        if known_token_info[contract_id]["contract_type"] == "TokenContract":
            return SEND_NORMAL_TYPE_TOKEN_FUNC_INDEX
        elif known_token_info[contract_id]["contract_type"] == "TokenContractWithSplit":
            return SEND_SPLIT_TYPE_TOKEN_FUNC_INDEX
    return None


def send_data_stack_gen(contract_id, recipient, amount):
    if known_token_info.get(contract_id):
        if known_token_info[contract_id]["contract_type"] == "TokenContract":
            return token_without_split_helper.send_data_stack_generator(recipient, amount)
        elif known_token_info[contract_id]["contract_type"] == "TokenContractWithSplit":
            return token_with_split_helper.send_data_stack_generator(recipient, amount)
    return None

def send_token(account, contract_id, amount, recipient):
    function_id = get_send_function_index(contract_id)
    send_data = send_data_stack_gen(contract_id, recipient, amount)
    info = account.execute_contract(contract_id, function_id, send_data)
    return info


if __name__ == "__main__":
    chain = pv.testnet_chain()
    pk = "XXXXXXXXXXX"
    contract_id = "CFEBiQG3WtJNWFhtEj1CDDg6Wy7G6P7BPSy"
    my_account = Account(chain=chain, private_key=pk)
    recipient = "AU18gPQDnhG3PqmdzLjUyRTM3t8bbffGPo8"
    unity = known_token_info[contract_id]["unity"]
    amount = int(0.2 * unity)  # send 0.2 Token

    # For hot wallet sending transaction
    resp = send_token(my_account, contract_id, amount, recipient)
    print(resp)

    # For cold wallet signing transaction
    pv.set_offline()
    resp = send_token(my_account, contract_id, amount, recipient)
    print(resp)

Sample result:

{
  'type': 9,
  'id': 'Abz3CgYtgQ7YEw7RGpCcSm8rZiLVNedGMRTpEovetcFX',
  'fee': 30000000,
  'feeScale': 100,
  'timestamp': 1572345441719799040,
  'proofs': [
    {
      'proofType': 'Curve25519',
      'publicKey': 'FumkPwX7CLon6Jhbf7rY1J3K6To5rjWwwrfaKsS55Fza',
      'address': 'AUCmDcp7BAwoc2DdWRwBcF4WJb1QWcrS9Mb',
      'signature': '58MK4PoMLjFHYTL8xGCCjRz64gtJ6MN6CN4E59XA7yzeY6ghCLgfTwnepu5CtaivY24zyZseFbcDYxjgGYfhHCXm'
    }
  ],
  'contractId': 'CFEBiQG3WtJNWFhtEj1CDDg6Wy7G6P7BPSy',
  'functionIndex': 3,
  'functionData': '14uNyNaSUdkRdg2UzkyBz2QwKSe1GPh8gNpExZGdsL2vTYePVTD',
  'attachment': ''
}

Confirm the transaction in Chain

Call check_tx method with transaction id to confirm. The confirmations should set to M * super_node_num + 1 (M = max accept waiting minutes since transaction sent). As the short-term fork case would be happened, some blocks have chance to be rolled back. So the greater confirmations you set, the less false confirm in chain case will be occurred. Currently, super node num is 15. So we suggest the confirmations set more than 15. For example, if you could wait 2 minutes to confirm a transaction, which just sent, in chain, then confirmations should be 2*15+1=31:

import pyvsystems as pv
from pyvsystems import Account

my_account = Account(chain=pv.testnet_chain())
tx_id = "dE4s1joxLqP1sSVRBB1KmEfB6a3vND2k3Uwsao12paG"
confirm_result = my_account.check_tx(tx_id, 31)
print(confirm_result)

If Transaction is fully confirmed, it will return True. If Transaction is sent but not confirmed or failed, it will return False. If Transaction does not exist, it will return None.

Check Transaction History by Address

import pyvsystems as pv
from pyvsystems import Account
import datetime
from pyvsystems.error import *
import base58
from pyvsystems.setting import EXECUTE_CONTRACT_FUNCTION_TX_TYPE
from pyvsystems.data_entry import data_entry_from_base58_str

SEND_NORMAL_TYPE_TOKEN_FUNC_INDEX = 3
SEND_SPLIT_TYPE_TOKEN_FUNC_INDEX = 4

# Known token info should store in DB
known_token_info = {
    "CFEBiQG3WtJNWFhtEj1CDDg6Wy7G6P7BPSy": {
        "token_id": "TWurhe6uRbuUw566dKm89Jrc9whSkUMHSjNYtFEJn",
        "token_name": "Token A",
        "contract_type": "TokenContract",
        "unity": 100000000
    },
    "CF89wmAjkThqK4KzxSBYjeWe2mKaF61vzfF": {
        "token_id": "TWuBEsV7iRc7qzZbeM2BWU4R5PhTWVBBbc6wdfQBs",
        "token_name": "Token B",
        "contract_type": "TokenContractWithSplit",
        "unity": 1000000
    }
}


def get_send_function_index(contract_id):
    if known_token_info.get(contract_id):
        if known_token_info[contract_id]["contract_type"] == "TokenContract":
            return SEND_NORMAL_TYPE_TOKEN_FUNC_INDEX
        elif known_token_info[contract_id]["contract_type"] == "TokenContractWithSplit":
            return SEND_SPLIT_TYPE_TOKEN_FUNC_INDEX
    return None


def check_v_coin_transfer_history(my_account, ts_chain):
    try:
        print("========Check V Coin transfer history===========")
        history = my_account.get_tx_history(10)
        print("JSON result: {}".format(history))
        for record in history:
            if not record.get('proofs'):
                continue
            sender_public_key = base58.b58decode(record['proofs'][0]['publicKey'])
            sender_address = ts_chain.public_key_to_address(sender_public_key)
            if sender_address == my_account.address:
                print("####### Send #######")
            else:
                print("####### Received #######")
            print("From: {}".format(sender_address))
            print("To: {}".format(record['recipient']))
            print("Transaction ID: {}".format(record['id']))
            display_time = datetime.datetime.fromtimestamp(record['timestamp'] // 1000000000)
            print("Time: {}".format(display_time))
            print("Amount: {}".format(record['amount']))
            print("Transaction fee: {}".format(record['fee']))
    except InvalidParameterException as ex:
        # Handle Invalid Parameter issue here
        print("Invalid Parameter: {}".format(ex))
        return False
    except MissingAddressException:
        # Handle Address issue here
        print("No address for `my_account`")
        return False
    except NetworkException as ex:
        # Handle Network issue here
        print("Failed to get HTTP response: {}".format(ex))
        return False
    return True


def check_token_transfer_history(my_account, ts_chain):
    try:
        print("========Check token transfer history===========")
        history = my_account.get_tx_history(10, EXECUTE_CONTRACT_FUNCTION_TX_TYPE)
        print("JSON result: {}".format(history))
        for record in history:
            if not record.get('proofs') or not record.get('contractId') or not record.get('functionIndex'):
                print("[Warning] Unexpected tx record: {}".format(record))
                continue
            send_func_idx = get_send_function_index(record['contractId'])
            if not send_func_idx or record['functionIndex'] != send_func_idx:
                continue
            [recipient, amount] = data_entry_from_base58_str(record['functionData'])
            sender_public_key = base58.b58decode(record['proofs'][0]['publicKey'])
            sender_address = ts_chain.public_key_to_address(sender_public_key)
            token_name = known_token_info[record['contractId']]["token_name"]
            if sender_address == my_account.address:
                print("####### Send {} #######".format(token_name))
            else:
                print("####### Received {} #######".format(token_name))
            print("From: {}".format(sender_address))
            print("To: {}".format(recipient.data))
            print("Transaction ID: {}".format(record['id']))
            display_time = datetime.datetime.fromtimestamp(record['timestamp'] // 1000000000)
            print("Time: {}".format(display_time))
            print("Amount: {}".format(amount.data))
            print("Transaction fee: {}".format(record['fee']))
    except InvalidParameterException as ex:
        # Handle Invalid Parameter issue here
        print("Invalid Parameter: {}".format(ex))
        return False
    except MissingAddressException:
        # Handle Address issue here
        print("No address for `my_account`")
        return False
    except NetworkException as ex:
        # Handle Network issue here
        print("Failed to get HTTP response: {}".format(ex))
        return False
    return True


if __name__ == "__main__":
    address = 'AU4mSZPtUwDD9sD3bT1TVBkgcZiq3SgE1n1'
    chain = pv.testnet_chain()
    acc = Account(chain=chain, address=address)
    check_v_coin_transfer_history(acc, chain)
    check_token_transfer_history(acc, chain)