What's New: v1.44:
v1.43:
v1.42:
v1.41:
v1.4:
v1.34:
v1.33:
Web3 web3 = new Web3(GOERLI_ID)
.For a working demonstration of TokenScript and Web3E please try minting an ERC5169/TokenScript powered ERC721 NFT here: http://smarttokenlabs.duckdns.org You will need AlphaWallet on your mobile phone; simply replace the main.cpp in the project here: https://github.com/TokenScript/TokenScriptTestContracts/tree/master/firmware with the generated project which the Token wizard generates.
Web3E is a fully functional Web3 framework for Embedded devices running Arduino. Web3E now has methods which allow you to use TokenScript in your IoT solution for rapid deployment. Tested mainly on ESP32 and working on ESP8266. Also included is a rapid development DApp injector to convert your embedded server into a fully integrated Ethereum DApp.
Starting from a simple requirement - write a DApp capable of running on an ESP32 which can serve as a security door entry system. Some brave attempts can be found in scattered repos but ultimately even the best are just dapp veneers or have ingenous and clunky hand-rolled communication systems like the Arduino wallet attempts.
What is required is a method to write simple, fully embedded DApps which give you a zero infrastructure and total security solution. It is possible that as Ethereum runs natively on embedded devices a new revolution in the blockchain saga will begin. Now you have the ability to write a fully embedded DApp that gives you the security and flexibility of Ethereum in an embedded device.
Fixed transaction send to use EIP-155.
Reduce firmware size footprint.
Reduce dependency on external libraries.
Produce Interface wizard which creates an NFT, writes and uploads TokenScript to IPFS, links the TokenScript via EIP-5169 and writes the firmware. Wizard is here: http://smarttokenlabs.duckdns.org/ (Note that it is http, you need to trust the site!)
Improve encoding and decoding for calling smartcontract functions.
vastly improve SmartLayer comms with TokenScript.
Test examples with latest library and improve code.
Simplified Node connection.
Add TCP Bridge.
improve device key init.
add August lock crypto interface sample.
TokenScript/API interface TokenScript
uint256 class added to correctly handle Ethereum types.
usability methods added for converting between doubles and Wei values.
usability methods added for displaying Wei values as doubles.
random number generation uses Mersenne Twister.
memory usage improved.
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
; Serial Monitor options
monitor_speed = 115200
lib_ldf_mode = deep
lib_deps =
# Using a library name
Web3E
Please note: you may need to use 'lib_ldf_mode = deep' to locate some ESP32 libraries like 'EEPROM.h' also: if you use many other libraries in your application you may need to add 'board_build.partitions = no_ota.csv' to fit your firmware
The advantage of using TokenScript rather than a dapp is evident from looking at the code example. You will have a very nice user interface defined with html/css with the calling code written in small JavaScript functions. The user would quickly find the entry token in their wallet.
Web3E now has a series of API key free node endpoints for many EVMs. The full list can be found in src/chainIds.h
.
Supply one of these to the Web3 object in your main sketch to start connecting.
eg:
Web3 *web3 = new Web3(SEPOLIA_ID);
Web3 *web3 = new Web3(MAINNET_ID);
Web3 *web3 = new Web3(MUMBAI_TEST_ID);
Note, if you have an Infura API key and wish to use Infura where possible, edit Web3.h like this:
#define USING_INFURA 1
#define INFURA_KEY "1234567890abcdef1234567890abcdef" //<--- your Infura key here
Web3E will adjust the node endpoint to use Infura for all Infura supported networks. Note that you will need to enable Infura in the dashboard for all these networks; otherwise use the free connections.
https://github.com/alpha-wallet/Web3E-Application
Full source code for the system active at the AlphaWallet office. To get it working you need:
The push transaction sample requires a little work to get running. You have to have an Ethereum wallet, some testnet ETH, the private key for that testnet eth, and then create some ERC20 and ERC875 tokens in the account.
See Wallet Bridge 1, 2 and 3 examples for complete source
In this usage pattern, your IoT device will connect to a proxy server which provides a bridge to the TokenScript running on your wallet. The source code for the proxy server can be found here: Script Proxy
Set up API routes
const char *apiRoute = "api/";
enum APIRoutes {
api_getChallenge,
api_checkSignature,
api_End };
s_apiRoutes["getChallenge"] = api_getChallenge;
s_apiRoutes["checkSignature"] = api_checkSignature;
s_apiRoutes["end"] = api_End;
Declare TcpBridge, KeyID and Web3 in globals:
TcpBridge *tcpConnection;
Web3 *web3;
KeyID *keyID;
Setup your Web node
web3 = new Web3(MAINNET_ID);
Start UDP bridge after connecting to WiFi:
tcpConnection = new TcpBridge();
tcpConnection->setKey(keyID, web3);
tcpConnection->startConnection();
Within your loop() check for API call:
tcpConnection->checkClientAPI(&handleTCPAPI);
Handle API call in the callback:
enum APIRoutes
{
api_unknown,
api_getChallenge,
api_checkSignature,
api_checkSignatureLock,
api_checkMarqueeSig,
api_end
};
std::map<std::string, APIRoutes> s_apiRoutes;
void Initialize()
{
s_apiRoutes["getChallenge"] = api_getChallenge;
s_apiRoutes["checkSignature"] = api_checkSignature;
s_apiRoutes["checkSignatureLock"] = api_checkSignatureLock;
s_apiRoutes["end"] = api_end;
}
std::string handleTCPAPI(APIReturn* apiReturn)
{
switch (s_apiRoutes[apiReturn->apiName])
{
case api_getChallenge:
Serial.println(currentChallenge.c_str());
udpBridge->sendResponse(currentChallenge, methodId);
break;
case api_checkSignature:
{
//EC-Recover address from signature and challenge
string address = Crypto::ECRecoverFromPersonalMessage(&apiReturn->params["sig"], ¤tChallenge);
//Check if this address has our entry token
boolean hasToken = QueryBalance(&address);
updateChallenge(); //generate a new challenge after each check
if (hasToken)
{
udpBridge->sendResponse("pass", methodId);
OpenDoor(); //Call your code that opens a door or performs the required 'pass' action
}
else
{
udpBridge->sendResponse("fail: doesn't have token", methodId);
}
}
break;
In this usage pattern, the TokenScript running on the wallet will connect directly to the IoT device. Notice that this means your IoT is directly accessible to the internet, which may be susceptible to exploit.
Declare the TCP server in globals:
WiFiServer server(8082);
Ensure your Device is locked to a fixed IP Address for port forwarding (adjust local IP address as required):
IPAddress ipStat(192, 168, 1, 100);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress dns(192, 168, 1, 1);
WiFi.config(ipStat, gateway, subnet, dns, dns);
Set up API routes
const char *apiRoute = "api/";
enum APIRoutes {
api_getChallenge,
api_checkSignature,
api_End };
s_apiRoutes["getChallenge"] = api_getChallenge;
s_apiRoutes["checkSignature"] = api_checkSignature;
s_apiRoutes["end"] = api_End;
Listen for API call:
WiFiClient c = server.available(); // Listen for incoming clients
ScriptClient *client = (ScriptClient*) &c;
if (*client)
{
Serial.println("New Client.");
client->checkClientAPI(apiRoute, &handleAPI); //method handles connection close etc.
}
Handle API return:
void handleAPI(APIReturn *apiReturn, ScriptClient *client)
{
switch(s_apiRoutes[apiReturn->apiName.c_str()])
{
case api_getChallenge:
client->print(currentChallenge.c_str());
break;
case api_checkSignature:
{
//EC-Recover address from signature and challenge
string address = Crypto::ECRecoverFromPersonalMessage(&apiReturn->params["sig"], ¤tChallenge);
//Check if this address has our entry token
boolean hasToken = QueryBalance(&address);
updateChallenge(); //generate a new challenge after each check
if (hasToken)
{
client->print("pass");
OpenDoor(); //Call your code that opens a door or performs the required 'pass' action
}
else
{
client->print("fail: doesn't have token");
}
}
break;
}
// Setup Web3 and Contract with Private Key
...
web3 = new Web3(RINKEBY_ID);
Contract contract(web3, "");
contract.SetPrivateKey(PRIVATE_KEY);
uint32_t nonceVal = (uint32_t)web3->EthGetTransactionCount(&address); //obtain the next nonce
uint256_t weiValue = Util::ConvertToWei(0.25, 18); //send 0.25 eth
unsigned long long gasPriceVal = 1000000000ULL;
uint32_t gasLimitVal = 90000;
string emptyString = "";
string toAddress = "0xC067A53c91258ba513059919E03B81CF93f57Ac7";
string result = contract.SendTransaction(nonceVal, gasPriceVal, gasLimitVal, &toAddress, &weiValue, &emptyString);
uint256_t balance = web3->EthGetBalance(&address); //obtain balance in Wei
string balanceStr = Util::ConvertWeiToEthString(&balance, 18); //get string balance as Eth (18 decimals)
string address = string("0x007bee82bdd9e866b2bd114780a47f2261c684e3");
Contract contract(web3, "0x20fe562d797a42dcb3399062ae9546cd06f63280"); //contract is on Ropsten
//Obtain decimals to correctly display ERC20 balance (if you already know this you can skip this step)
string param = contract.SetupContractData("decimals()", &address);
string result = contract.ViewCall(¶m);
int decimals = web3->getInt(&result);
//Fetch the balance in base units
param = contract.SetupContractData("balanceOf(address)", &address);
result = contract.ViewCall(¶m);
uint256_t baseBalance = web3->getUint256(&result);
string balanceStr = Util::ConvertWeiToEthString(&baseBalance, decimals); //convert balance to double style using decimal places
string contractAddr = "0x20fe562d797a42dcb3399062ae9546cd06f63280";
Contract contract(web3, contractAddr.c_str());
contract.SetPrivateKey(<Your private key>);
//Get contract name
string param = contract.SetupContractData("name()", &addr);
string result = contract.ViewCall(¶m);
string interpreted = Util::InterpretStringResult(web3->getString(&result).c_str());
Serial.println(interpreted.c_str());
//Get Contract decimals
param = contract.SetupContractData("decimals()", &addr);
result = contract.ViewCall(¶m);
int decimals = web3->getInt(&result);
Serial.println(decimals);
unsigned long long gasPriceVal = 22000000000ULL;
uint32_t gasLimitVal = 4300000;
//amount of erc20 token to send, note we use decimal value obtained earlier
uint256_t weiValue = Util::ConvertToWei(0.1, decimals);
//get nonce
uint32_t nonceVal = (uint32_t)web3->EthGetTransactionCount(&addr);
string toAddress = "0x007bee82bdd9e866b2bd114780a47f2261c684e3";
string valueStr = "0x00";
//Setup contract function call
string p = contract.SetupContractData("transfer(address,uint256)", &toAddress, &weiValue); //ERC20 function plus params
//push transaction to ethereum
result = contract.SendTransaction(nonceVal, gasPriceVal, gasLimitVal, &contractAddr, &valueStr, &p);
string transactionHash = web3->getString(&result);
Originally forked https://github.com/kopanitsa/web3-arduino but with almost a complete re-write it is a new framework entirely.
Libraries used:
Coming soon:
If you support the cause, we could certainly use donations to help fund development:
0xbc8dAfeacA658Ae0857C80D8Aa6dE4D487577c63