B4J Library Web3X - Develop Web3 solutions with B4X

Status
Not open for further replies.
firefox_0Lzb5VCeAt.png

(image source: https://ethereum.org/en/)

You have probably all heard about Bitcoin, Ethereum, NFT and other blockchain technologies. For the outside viewer these technologies might look like one big ponzi scheme. This is what I thought up to a few months ago.
However, as I started to read and learn, I found a very interesting technology ecosystem. The Ethereum blockchain is especially interesting for us, the developers, as it is a blockchain with built-in programming language.
I'm not an oracle and I don't know whether Web3 will be a real thing in the near future. I do know that these technologies are very developer-centric and there can be interesting opportunities for creative developers.

I've started with wrapping Web3j - an open source Java library for integration of Ethereum clients: https://github.com/web3j/web3j
web3.js is a similar JavaScript library. It is more popular and I find it to be better documented: https://web3js.readthedocs.io/en/v1.5.2/

The Web3 concept is made of several technologies. There are many online tutorials about these technologies. You should start with learning the basics, they aren't tied to any specific programming language.

Web3X is an open source library written in B4X and based on Web3j. The source code is available here: https://github.com/AnywhereSoftware/Web3X
This is a B4J and B4A library.

Installation instructions:

1. Download the complete project: https://github.com/AnywhereSoftware/Web3X/archive/refs/heads/main.zip
2. Copy Web3X.b4xlib to the additional libraries folder.
3. Unzip web3x_dependencies.zip and copy to the additional libraries folder. Make sure to keep the web3 folder.
4. Other dependencies:
BigNumbers v1.3+: https://www.b4x.com/android/forum/threads/bignumbers-library.9540/#content
OkHttp v1.5+: www.b4x.com/android/files/okhttp-1.5.zip (internal library)


Supported features:
  • Creating and loading wallet files.
  • Signing messages.
  • Verifying signatures.
  • Connecting to Ethereum node with Infura: https://infura.io/ (trivial to add other providers)
  • Ethereum methods: EthBlockNumber, EthGetBalance, EthGetGasPrice, EnsResolve (ens name -> address), EnsReverseResolve (address -> ens name)
  • SendFunds
  • Utilities to convert between wei, ether and other units.
  • Utilities to create BigIntegers and BigDecimals. Most methods expect BigIntegers when dealing with numbers. The values, usually measured in weis, are too large to be held in the standard numeric types.
  • More to come.
Most of the methods are asynchronous and should be called with Wait For.
Example:
B4X:
Wait For (utils.LoadWallet(WalletFile, Password)) Complete (Result As W3AsyncResult)
If Result.Success Then
 Dim credentials As W3Credentials = Result.Value
 ...
End If
Wait For (Web3MainNet.EthBlockNumber) Complete (Result As W3AsyncResult)
If Result.Success Then
    Dim BlockNumber As BigInteger = Result.Value
    Log(BlockNumber)
End If
The type of Result.Value is described in the methods documentation.

Security concerns:

- Never post your private key. Anyone with the private key can retrieve all the account funds.
- Never lose the private key / wallet / password. They are non-recoverable.
- Start with one of the testnets.
- Use a test account during development.

I will post more examples soon. Please feel free to start a new thread for any question or comment (general discussion here: https://www.b4x.com/android/forum/threads/web3x-ethereum-questions.137353/)

The current version is 0.86 and it is considered a beta version.
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Lets start. The current features are quite simple to use. Make sure to use the latest version of Web3X.

Create an account with infura: https://infura.io/ and create a new project.
Choose one of the testnets. I've started with ropsten.
Tip: working faucet - https://faucet.dimensions.network/

Lets see that it works:
Create a new B4XPages project.
B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Private Web3Ropsten As Web3X
    Private Utils As W3Utils
    Private InfuraRopstenLink As String = "https://ropsten.infura.io/v3/your_project_id_here"
End Sub

Public Sub Initialize
End Sub

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    Utils.Initialize
    Web3Ropsten = Utils.BuildWeb3Infura(InfuraRopstenLink)
    Wait For (Web3Ropsten.EthBlockNumber) Complete (Result As W3AsyncResult)
    If Result.Success Then
        Log("Current block: " & Result.Value)
    End If
End Sub

Create a wallet file:
You can create a new wallet file with a new private key or import an existing private key (for example from MetaMask).
B4X:
Dim sf As Object = Utils.GenerateNewWallet("C:\Users\H\Downloads", "secret password", True)
Wait For (sf) Complete (Result As W3AsyncResult)
If Result.Success Then
    Log("Wallet path: " & Result.Value)
End If
The last parameter - 'Light', determines the wallet encryption strength.

Creating a new wallet with an existing private key:
B4X:
Dim PrivateKey As BigInteger = Utils.BigIntFromHex("bbbbbbbbbbccccc691fb21f07c9f4a66f3bda8da703c158c33fffffff")
Dim sf As Object = Utils.GenerateWalletWithPrivateKey("C:\Users\H\Downloads", "secret password", PrivateKey.ToByteArray, True)
Wait For (sf) Complete (Result As W3AsyncResult)
If Result.Success Then
    Log("Wallet path: " & Result.Value)
End If

Load a wallet file:
B4X:
Dim Path As String = "C:\Users\H\Downloads\UTC--2022-01-05T15-53-07.603000000Z--bbbbbbbbbbccccc691fb21f07c9f4a66f3bda8da703c158c33fffffff.json"
Wait For (Utils.LoadWallet(Path, "secret password")) Complete (Result As W3AsyncResult)
If Result.Success Then
    Dim Credentials As W3Credentials = Result.Value
    Log(Credentials.Address)
End If

Check your balance:
It is a good opportunity to remind that Result.Value type is described in the methods documentation:
1641398295284.png

B4X:
Wait For (Web3Ropsten.EthGetBalance(Credentials.Address, Utils.BLOCK_LATEST)) Complete (Result As W3AsyncResult)
If Result.Success Then
    Dim wei As BigInteger = Result.Value
    Log(Utils.ConvertFromWei(wei, "ether") & " ether")
End If

More examples tomorrow.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Signing and verifying signatures

This is a simple and powerful feature. It doesn't depend on anything external. It uses the private key to sign the message, and later the public key is used to verify the signature integrity.

Signing:
B4X:
Dim message() As Byte = "this is the message".GetBytes("utf8")
Dim signature() As Byte = utils.SignPrefixedMessage(message, Credentials)
Signature algorithm: https://web3js.readthedocs.io/en/v1.5.2/web3-eth-personal.html#sign

Anyone holding the original message and signature can verify whether the message was signed by a specific address:
B4X:
Dim IsVerified As Boolean = utils.VerifySignature(message, signature, "0xbbbbbbbbbbccccc691fb21f07c9f4a66f3bda8da703c158c33fffffff")
You can use it for example, to authenticate a user. The server sends a message to the client. Client sends back the signature and address. Server verifies the signature.

There is another relevant method - ExtractPublicKeysFromSignature. This methods extracts the possible public keys of the credentials that were used to sign the message.
It will return a list with 0 to 2 BigIntegers (https://crypto.stackexchange.com/qu...g-the-public-key-from-an-ecdsa-signature-work).
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Sending funds

The process of sending funds between accounts is made of several steps, some of these steps are done in the library code, but as developers it is still important to understand the complete process.

B4X:
Private Sub SendFunds
    'https://ethgasstation.info/
    'Gas fee will be Min(MaxFeePerGas * 21,000, Base fee + MaxPriorityFeePerGas)
    Dim ToAddress As String = "0xbbbbbbbbbbccccc691fb21f07c9f4a66f3bda8da703c158c33fffffff"
    Dim Amount As BigInteger = utils.BigIntFromUnit("0.01", "ether")
    Dim MaxPriorityFeePerGas As BigInteger = utils.BigIntFromUnit("3", "gwei")
    Dim MaxFeePerGas As BigInteger = utils.BigIntFromUnit("200", "gwei")
    Dim ChainId As Int = utils.CHAINID_ROPSTEN
    Wait For (Web3Ropsten.SendFunds(ChainId, Credentials, ToAddress, Amount, MaxPriorityFeePerGas, MaxFeePerGas)) Complete (Result As W3AsyncResult)
    If Result.Success Then
        Dim hash As W3TransactionHash = Result.Value
        Log("Transaction sent successfully: ")
        Log(hash.Hash)
        Dim Timeout As Int = 300 'seconds
        Wait For (Web3Ropsten.EthGetTransactionReceipt(hash, Timeout)) Complete (Result As W3AsyncResult)
        If Result.Success Then
            Dim tr As W3TransactionReceipt = Result.Value
            If tr.Pending Then
                Log("Transaction still pending")
            Else
                Log("Transaction completed, ok? " & tr.StatusOk)
                Log(tr)
            End If
        Else
            Log("Error getting receipt: " & Result.Error)
        End If
    End If
End Sub

Nonce

Every transaction includes a nonce value. In Ethereum, the nonce is the number of transactions sent from the account.
The purpose of this value is to prevent "replay attacks".
The SendFunds method, gets the nonce value from the node.
SendFunds2 method allows developers to set the nonce value. This allows replacing a pending transaction with a different one (speed up / cancel).
Note that transactions are processed in order. This means that a "stuck" transaction will cause all later transactions to wait as well.

Gas

Web3X.SendFunds is based on EIP 1559. A good explanation of the gas model: https://ethereum.org/en/developers/docs/gas/#eip-1559
As written in the comment above, the gas fee for ETH transfer is: Min(MaxFeePerGas * 21,000, Base fee + MaxPriorityFeePerGas)
If it will be too low then the transaction will not be processed.
You can see the base fee and the recommended miner tip(=MaxPriorityFeePerGas) here: https://ethgasstation.info/

SendFunds

This method gets the nonce, creates the transaction message, signs it with the account's private key and sends it to the node.
It returns a W3TransactionHash object. This object holds two values: the transaction hash and the transaction nonce.
At this point the transaction is pending. If the gas was set properly then it can take it anywhere from a few seconds to a few minutes to complete. If not then it will be pending forever.

Get receipt

Now its time to call EthGetTransactionReceipt with the transaction hash and a timeout value (measured in seconds).
This method will poll the node until the transaction completes or the timeout is reached.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Make sure to use latest version of Web3X.

Ganache is a personal Ethereum blockchain: https://trufflesuite.com/ganache/
It is very simple to install and run, and it is a great way to experiment with Web3X.

Create a workspace with the default settings and you will see something like:

1641998155936.png


The attached program allows sending Ether between accounts. Naturally, you need to have the private key of the source account. Click on the key button next to the account and copy the private key.
Remember that anyone with access to the private key has access to the account. In this case these are test accounts but it is a good habit to always be careful when working with those keys.

1641998438524.png



Notes

- The current version of Ganache doesn't support the relatively new EIP 1559 protocol, so we are using Web3.SendFundsLegacy with the gas price, instead of setting the priority fee + max price.
- The Web3 object is created with Utils.BuildWeb3Http(HttpLink). This is different than with Infura where we used BuildWeb3Infura.
 

Attachments

  • Project.zip
    9.8 KB · Views: 322

Erel

B4X founder
Staff member
Licensed User
Longtime User
Client / server authentication example

Lets start with the communication flow:

1. Client sends a request to the getmessage endpoint. Server responds with a random message. The server also stores the random message in the user session (=server memory).
2. Clients signs the message and sends the address + signature to the authenticate endpoint. Server verifies the signature and returns the result. Server also stores the user state (verified and address) in the user session.

Client code:
B4X:
Private Sub GetMessage As ResumableSub
    Dim msg() As Byte
    Dim j As HttpJob
    j.Initialize("", Me)
    j.Download(ServerUrl & "/getmessage")
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        Dim message As String = j.GetString.As(JSON).ToMap.Get("message")
        msg = bc.HexToBytes(message)
    End If
    j.Release
    If j.Success = False Then Return False
    Wait For (Authenticate(msg)) Complete (Success As Boolean)
    Return Success
End Sub

Private Sub Authenticate (msg() As Byte) As ResumableSub
    Dim signature() As Byte = Utils.SignPrefixedMessage(msg, Credentials)
    Dim j As HttpJob
    j.Initialize("", Me)
    Dim m As Map = CreateMap("address": Credentials.Address, "signature": bc.HexFromBytes(signature))
    j.PostString(ServerUrl & "/authenticate", m.As(JSON).ToCompactString)
    Wait For (j) JobDone (j As HttpJob)
    Dim verified As Boolean = False
    If j.Success Then
        Dim m As Map = j.GetString.As(JSON).ToMap
        verified = m.Get("verified")
    End If
    j.Release
    Return verified
End Sub
The two projects are attached.
You can run both projects with two instances of B4J. At least one of the instances should be in release mode.
The server project depends on Encryption library: https://www.b4x.com/android/forum/threads/6839/#content it uses SecureRandom to generate the random message.
 

Attachments

  • Client.zip
    9.3 KB · Views: 295
  • Server.zip
    3.5 KB · Views: 266
Last edited:
Status
Not open for further replies.
Top