My first client
This tutorial is an introduction to client development on testnet using the official SDKs.
Getting Started#
In this tutorial, we demonstrate the key elements of a basic client using the official SDKs to interact with the Blockchain. The code for the tutorial is available here: my-first-client. The code in this project can be run from the root of the project directory by issuing the make command.
The example code uses the official Client SDKs. Currently, Go, Java, and Python are available. These libraries are developed to simplify aspects of the development process. If your language is not currently supported, or on the upcoming roadmap (Rust), then you will want to refer to the low-level JSON-RPC API. To request additional functionality or to track when it is implemented, you can submit a GitHub issue on the corresponding project repository.
To see advanced usage, refer to the Reference Wallet project.
Setup#
All code examples are shared in the my-first-client repo on GitHub.
Clone the repo:#
git clone [https://github.com/diem/my-first-client.git](https://github.com/diem/my-first-client.gitEach SDK has the following system requirements:
- Java: Java 8+
- Python: Python v3.7+, pipenv
Run the examples:#
make
or
make python for Python
make java for Java
Connecting to the network#
The first thing your client code will need to do is connect to the network.
- Python
- Java
client = testnet.create_client();client = Testnet.createClient();Creating wallets#
Wallets are addresses on the Blockchain that may issue transactions that send or receive funds. Private and Public key are needed.
- Python
- Java
# generate private key for sender accountsender_private_key = Ed25519PrivateKey.generate()
# generate auth key for sender accountsender_auth_key = AuthKey.from_public_key(sender_private_key.public_key())print(f"Generated sender address: {utils.account_address_hex(sender_auth_key.account_address())}")// generate private key for sender accountPrivateKey senderPrivateKey = new Ed25519PrivateKey(new Ed25519PrivateKeyParameters(new SecureRandom()));
// generate auth key for sender accountAuthKey senderAuthKey = AuthKey.ed24419(senderPrivateKey.publicKey());Requesting XUS from the faucet#
A faucet is a blockchain tool for testing blockchain transactions by providing coins for testing. When instructed, the testnet faucet will send the requested amount of XUS to the address provided.
Here, we request 100 XUS from the faucet and deposit it in Wallet A.
- Python
- Java
faucet = testnet.Faucet(client)faucet.mint(sender_auth_key.hex(), 10000000, "XUS")Testnet.mintCoins(client, 10000000, senderAuthKey.hex(), "XUS");Getting a balance#
In the previous step we requested 100 XUS from the faucet. We can verify that our test wallet now has the expected balance.
- Python
- Java
# connect to testnetclient = testnet.create_client()
# generate private keyprivate_key = Ed25519PrivateKey.generate()
# generate auth keyauth_key = AuthKey.from_public_key(private_key.public_key())print(f"Generated address: {utils.account_address_hex(auth_key.account_address())}")
# create accountfaucet = testnet.Faucet(client)faucet.mint(auth_key.hex(), 1340000000, "XUS")
# get account informationaccount = client.get_account(auth_key.account_address())print("Account info:")print(account)//connect to testnetDiemClient client = Testnet.createClient();
//generate private key for new accountPrivateKey privateKey = new Ed25519PrivateKey(new Ed25519PrivateKeyParameters(new SecureRandom()));
//generate auth key for new accountAuthKey authKey = AuthKey.ed24419(privateKey.publicKey());
//create accountTestnet.mintCoins(client, 10000000, authKey.hex(), CURRENCY_CODE);
//get account informationAccount account = client.getAccount(authKey.accountAddress());System.out.println("Account info:");System.out.println(account);Sending Coins#
Note: There are several types of peer to peer transactions. These transactions shall have regulatory and compliance requirements that must be followed. To learn more about requirements, please visit the Prospective VASPs document here.
Next we demonstrate sending 10 units of XUS from a Sender wallet to a Receiver wallet.
- Python
- Java
# connect to testnetclient = testnet.create_client()
# generate private key for sender accountsender_private_key = Ed25519PrivateKey.generate()
# generate auth key for sender accountsender_auth_key = AuthKey.from_public_key(sender_private_key.public_key())print(f"Generated sender address: {utils.account_address_hex(sender_auth_key.account_address())}")
# create sender accountfaucet = testnet.Faucet(client)testnet.Faucet.mint(faucet, sender_auth_key.hex(), 100000000, "XUS")
# get sender accountsender_account = client.get_account(sender_auth_key.account_address())
# generate private key for receiver accountreceiver_private_key = Ed25519PrivateKey.generate()
# generate auth key for receiver accountreceiver_auth_key = AuthKey.from_public_key(receiver_private_key.public_key())print(f"Generated receiver address: {utils.account_address_hex(receiver_auth_key.account_address())}")
# create receiver accountfaucet = testnet.Faucet(client)faucet.mint(receiver_auth_key.hex(), 10000000, CURRENCY)
# create scriptscript = stdlib.encode_peer_to_peer_with_metadata_script(    currency=utils.currency_code(CURRENCY),    payee=receiver_auth_key.account_address(),    amount=10000000,    metadata=b'',  # no requirement for metadata and metadata signature    metadata_signature=b'',    )
# create transactionraw_transaction = diem_types.RawTransaction(    sender=sender_auth_key.account_address(),    sequence_number=sender_account.sequence_number,    payload=diem_types.TransactionPayload__Script(script),    max_gas_amount=1_000_000,    gas_unit_price=0,    gas_currency_code=CURRENCY,    expiration_timestamp_secs=int(time.time()) + 30,    chain_id=CHAIN_ID,    )
# sign transactionsignature = sender_private_key.sign(utils.raw_transaction_signing_msg(raw_transaction))public_key_bytes = utils.public_key_bytes(sender_private_key.public_key())signed_txn = utils.create_signed_transaction(raw_transaction, public_key_bytes, signature)
# submit transactionclient.submit(signed_txn)
# wait for transactionclient.wait_for_transaction(signed_txn)//connect to testnetDiemClient client = Testnet.createClient();
//generate private key for sender accountPrivateKey senderPrivateKey = new Ed25519PrivateKey(new Ed25519PrivateKeyParameters(new SecureRandom()));
//generate auth key for sender accountAuthKey senderAuthKey = AuthKey.ed24419(senderPrivateKey.publicKey());
//create sender account with 100 XUS balanceTestnet.mintCoins(client, 100000000, senderAuthKey.hex(), CURRENCY_CODE);
//get sender account for sequence numberAccount account = client.getAccount(senderAuthKey.accountAddress());
//generate private key for receiver accountPrivateKey receiverPrivateKey = new Ed25519PrivateKey(new Ed25519PrivateKeyParameters(new SecureRandom()));
//generate auth key for receiver accountAuthKey receiverAuthKey = AuthKey.ed24419(receiverPrivateKey.publicKey());
//create receiver account with 1 XUS balanceTestnet.mintCoins(client, 10000000, receiverAuthKey.hex(), CURRENCY_CODE);
//create scriptTransactionPayload script = new TransactionPayload.Script(        Helpers.encode_peer_to_peer_with_metadata_script(                CurrencyCode.typeTag(CURRENCY_CODE),                receiverAuthKey.accountAddress(),                10000000L,                new Bytes(new byte[0]),                new Bytes(new byte[0])));
//create transaction to send 1 XUSRawTransaction rawTransaction = new RawTransaction(        senderAuthKey.accountAddress(),        account.getSequenceNumber(),        script,        1000000L,        0L,        CURRENCY_CODE,        (System.currentTimeMillis() / 1000) + 300,        CHAIN_ID);
//sign transactionSignedTransaction st = Signer.sign(senderPrivateKey, rawTransaction);
//submit transactiontry {    client.submit(st);} catch (StaleResponseException e) {    //ignore}
//wait for the transaction to completeTransaction transaction = client.waitForTransaction(st, 100000);System.out.println(transaction);We can verify that 10 XUS was sent by verifying the sender’s wallet balance is 90 and receiver’s balance is 10.
Transaction Intent (DIP-5)#
DIP-5 introduces a standard way for communicating information about a specific transaction. DIP-5 encodes a destination address, currency, and amount. You can use this information to handle the user’s intent.
Each SDK provides the following helper function for processing this information.
- Python
- Java
//generate private key for new accountPrivateKey privateKey = new Ed25519PrivateKey(new Ed25519PrivateKeyParameters(new SecureRandom()));
//generate auth key for new accountAuthKey authKey = AuthKey.ed24419(privateKey.publicKey());
//create IntentIdentifierAccountIdentifier accountIdentifier = new AccountIdentifier(TestnetPrefix, authKey.accountAddress());IntentIdentifier intentIdentifier = new IntentIdentifier(accountIdentifier, CURRENCY, 10000000L);String intentIdentifierString = intentIdentifier.encode();System.out.println("Encoded IntentIdentifier: " + intentIdentifierString);
//deserialize IntentIdentifierIntentIdentifier decodedIntentIdentifier = decode(TestnetPrefix, intentIdentifierString);
System.out.println("Account (HEX) from intent: " + Hex.encode(decodedIntentIdentifier.getAccountIdentifier().getAccountAddress().value));System.out.println("Amount from intent: " + decodedIntentIdentifier.getAmount());System.out.println("Currency from intent: " + decodedIntentIdentifier.getCurrency());# generate private keyprivate_key = Ed25519PrivateKey.generate()
# generate auth keyauth_key = AuthKey.from_public_key(private_key.public_key())
# create intent identifieraccount_identifier = identifier.encode_account(utils.account_address_hex(auth_key.account_address()), None, identifier.TLB)encoded_intent_identifier = identifier.encode_intent(account_identifier, "XUS", 10000000)print(f"Encoded IntentIdentifier: {encoded_intent_identifier}")
# deserialize IntentIdentifierintent_identifier = diem.identifier.decode_intent(encoded_intent_identifier, identifier.TLB)print(f"Account (HEX) from intent: {utils.account_address_hex(intent_identifier.account_address)}")print(f"Amount from intent: {intent_identifier.amount}")print(f"Currency from intent: {intent_identifier.currency_code}")Subscribing to events#
To monitor and react to transactions, you may subscribe to events.
In the example below, we will set up a wallet with 100 XUS and then call the mint to add 1 XUS ten times.
- Python
- Java
import timefrom random import randrangefrom threading import Threadfrom cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKeyfrom diem import testnet, AuthKey, utils
CURRENCY = "XUS"
"""get_events_example demonstrates how to subscribe to a specific events stream base on events key"""
def main():
    # connect to testnet    client = testnet.create_client()
    # generate private key    private_key = Ed25519PrivateKey.generate()
    # generate auth key    auth_key = AuthKey.from_public_key(private_key.public_key())    print(f"Generated address: {utils.account_address_hex(auth_key.account_address())}")
    # create new account    faucet = testnet.Faucet(client)    faucet.mint(auth_key.hex(), 100000000, CURRENCY)
    # get account events key    account = client.get_account(auth_key.account_address())    events_key = account.received_events_key
    # start minter to demonstrates events creation    start_minter(client, auth_key)
    # demonstrates events subscription    subscribe(client, events_key)
def subscribe_(client, events_key):    start = 0    for x in range(0, 15):        events = client.get_events(events_key, start, 10)        start += len(events)        print(f"{len(events)} new events found")        time.sleep(3)        for i in range(0, len(events)):            print(f"Event # {i + 1}:")            print(events[i])
def minter(client, auth_key):    for x in range(0, 10):        amount = 1000000        faucet = testnet.Faucet(client)        testnet.Faucet.mint(faucet, auth_key.hex(), amount, CURRENCY)        time.sleep(1)
def subscribe(client, events_key):    Thread(target=subscribe_, args=(client, events_key,)).start()
def start_minter(client, auth_key):    Thread(target=minter, args=(client, auth_key)).start()
if __name__ == "__main__":    main()
package example;import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;import org.diem.*;import org.diem.jsonrpctypes.JsonRpc.Account;import org.diem.jsonrpctypes.JsonRpc.Event;import java.security.SecureRandom;import java.util.List;import java.util.concurrent.ThreadLocalRandom;
/** * GetEventsExample demonstrates how to subscribe to a specific events stream base on events key */
public class GetEventsExample {   public static final String CURRENCY_CODE = "XUS";   public static void main(String[] args) throws DiemException {       //connect to testnet       DiemClient client = Testnet.createClient();       //create new account       SecureRandom random = new SecureRandom();       Ed25519PrivateKeyParameters privateKeyParams = new Ed25519PrivateKeyParameters(random);       Ed25519PrivateKey privateKey = new Ed25519PrivateKey(privateKeyParams);       AuthKey authKey = AuthKey.ed24419(privateKey.publicKey());       Testnet.mintCoins(client, 100000000, authKey.hex(), CURRENCY_CODE);       //get account events key       Account account = client.getAccount(authKey.accountAddress());       String eventsKey = account.getReceivedEventsKey();       //start minter to demonstrates events creation       startMinter(client, authKey);       //demonstrates events subscription       subscribe(client, eventsKey);   }   public static void subscribe(DiemClient client, String eventsKey) {       Runnable listener = () -> {           long start = 0;           for (int i = 0; i < 15; i++) {               List<Event> events;               try {                   events = client.getEvents(eventsKey, start, 10);               } catch (DiemException e) {                   throw new RuntimeException(e);               }               start += events.size();               System.out.println(events.size() + " new events found");               for (int j = 0; j < events.size(); j++) {                   System.out.println("Event #" + (j + 1) + ":");                   System.out.println(events.get(j));               }               try {                   Thread.sleep(3_000);               } catch (InterruptedException e) {                   throw new RuntimeException(e);               }           }       };       Thread listenerThread = new Thread(listener);       listenerThread.start();   }   private static void startMinter(DiemClient client, AuthKey authKey) {       Runnable minter = () -> {           for (int i = 0; i < 10; i++) {               int amount =  1000000;               Testnet.mintCoins(client, amount, authKey.hex(), CURRENCY_CODE);               try {                   Thread.sleep(1_000);               } catch (InterruptedException e) {                   throw new RuntimeException(e);               }           }       };       Thread minterThread = new Thread(minter);       minterThread.start();   }}