What are the ways to automatically receive an updated Ethereum wallet balance? - node.js

I looked at a lot of options on Google and besides how to subscribe to the event web3.eth.subscribe("newBlockHeaders"... didn't find anything or just didn't work. But I think this solution is the most resource-intensive and inefficient.
Tell me this is the only way I can implement? Are there any paid solutions?
My task is to track the balance of wallets and notify the user in case of an update.
By updating the balance, I mean when events occur in the wallet:
Transfer received
Transfer sent

This is a sample contract that has two functions that emit, "TransferReceived" and "TransferSent".
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
contract Sample {
event TransferReceived(address sender, uint amount);
event TransferSent(address sender, uint amount);
function SendTransfer() public payable{
emit TransferSent(msg.sender, msg.value);
}
function ReceiveTransfer(uint amount) public {
emit TransferReceived(msg.sender, amount);
}
}
This is the Javascript code to listen for both events on the sample contract. This example uses etherjs:
const contractAddress = process.env.CONTRACT_ADDRESS;
const provider = new ethers.providers.WebSocketProvider(process.env.WEB_SOCKET);
const contract = new ethers.Contract(contractAddress, abi, provider);
export async function handleEvents() {
contract.on("TransferReceived", async (sender, amount,event) => {
//Do Something
})
contract.on("TransferSent", async (sender, amount, event) => {
//Do something
});
}
You can get a WebSocket URL from node providers like Alchemy or Infura.
I hope this helps

Ogubuike Alex's answer is one solution and I commend him for it, but it also remains a resource intensive solution.
I also came across this library, which, through a smart contract, returns the balance of different tokens in the network.
https://www.npmjs.com/package/eth-balance-checker
And I came up with another option:
Using cron to send requests to the blockchain, which is more optimal and not costly in terms of resources.

Related

Solana program to send multiple lamport transfers and emit event

I’m building a program intended to manage multiple payments with one call. The program needs to complete the following steps:
Accept a certain amount of lamports
Pay a portion of the received lamports to specified wallets, such that the amount received is exhausted
Emit an event containing the receipt
I’ve built this logic with an Ethereum smart contract and it works perfectly fine, however when attempting to write a Solana program with Solang and #solana/solidity, I’m running into a number of issues.
The first issue I encountered was simply that #solana/solidity seems to not be built for front end use (transactions required a private key as an argument, rather than being signed by a wallet like Phantom) so I built a fork of the repository that exposes the transaction object to be signed. I also found that the signer’s key needed to be manually added to the array of keys in the transaction instruction — see this Stack Overflow post for more information, including the front end code used to sign and send the transaction.
However, after this post I ran into more errors, take the following for example:
Transaction simulation failed: Attempt to debit an account but found no record of a prior credit.
Transaction simulation failed: Error processing Instruction 0: instruction changed the balance of a read-only account
Program jdN1wZjg5P4xi718DG2HraGuxVx1mM7ebjXpxbJ5R3N invoke [1]
Program data: PO+eZwYByRZpDC4BOjWoKPj20gquFc/JtyxU9NsuG/Y= DEjYtM7vwjNW3HPewJU3dvG4aiov5tUUlrD6Zz5ylBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADppnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATEtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVNC02S0gyMV9Sa3RZZVJIb3FKOFpFAAAAAAAAAAAAAAA=
Program jdN1wZjg5P4xi718DG2HraGuxVx1mM7ebjXpxbJ5R3N consumed 3850 of 200000 compute units
Program jdN1wZjg5P4xi718DG2HraGuxVx1mM7ebjXpxbJ5R3N success
failed to verify account 11111111111111111111111111111111: instruction changed the balance of a read-only account
The error messages seemed to be inconsistent, with some attempts throwing different errors despite the only changes in the code being a server restarting or a library being reinstalled.
Although solutions to the previous errors would be greatly appreciated, at this point I’m more inclined to ask more broadly if what I’m trying to do is possible, and, providing the source code, for help understanding what I need to do to make it work.
Below is the working source code for my Ethereum contract:
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
contract MyContract {
event Receipt(
address From,
address Token,
address[] Receivers,
uint256[] Amounts,
string Payment
);
function send(
address[] calldata _receivers,
uint256[] calldata _amounts,
string calldata _payment
) external payable {
require(
_receivers.length == _amounts.length,
"Receiver count does not match amount count."
);
uint256 total;
for (uint8 i; i < _receivers.length; i++) {
total += _amounts[i];
}
require(
total == msg.value,
"Total payment value does not match ether sent"
);
for (uint8 i; i < _receivers.length; i++) {
(bool sent, ) = _receivers[i].call{value: _amounts[i]}("");
require(sent, "Transfer failed.");
}
emit Receipt(
msg.sender,
0x0000000000000000000000000000000000000000,
_receivers,
_amounts,
_payment
);
}
}
The only differences between this code and my Solana program code are types and the method used to transfer lamports. All references to uint256 is replaced by uint64, the placeholder token address is changed from the null address to the system public key (address"11111111111111111111111111111111"), and the payment loop is changed to the following:
for (uint8 i = 0; i < _receivers.length; i++) {
payable(_receivers[i]).transfer(_amounts[i]); // Using .send() throws the same error
}
The code used to then deploy the program to the Solana test validator is as follows, only slightly modified from the example provided by #solana/solidity:
const { Connection, LAMPORTS_PER_SOL, Keypair, PublicKey } = require('#solana/web3.js');
const { Contract } = require('#solana/solidity');
const { readFileSync } = require('fs');
const PROGRAM_ABI = JSON.parse(readFileSync('./build/sol/MyProgram.abi', 'utf8'));
const BUNDLE_SO = readFileSync('./build/sol/bundle.so');
(async function () {
console.log('Connecting to your local Solana node');
const connection = new Connection('http://localhost:8899', 'confirmed');
const payer = Keypair.generate();
async function airdrop(pubkey, amnt) {
const sig = await connection.requestAirdrop(pubkey, amnt * LAMPORTS_PER_SOL);
return connection.confirmTransaction(sig);
}
console.log('Airdropping SOL to a new wallet');
await airdrop(payer.publicKey, 100);
const program = new Keypair({
publicKey: new Uint8Array([...]),
secretKey: new Uint8Array([...])
});
const storage = new Keypair({
publicKey: new Uint8Array([...]),
secretKey: new Uint8Array([...])
});
const contract = new Contract(connection, program.publicKey, storage.publicKey, PROGRAM_ABI, payer);
console.log('Loading the program');
await contract.load(program, BUNDLE_SO);
console.log('Deploying the program');
await contract.deploy('MyProgram', [], program, storage, 4096 * 8);
console.log('Program deployed!');
process.exit(0);
})();
Is there something I’m misunderstanding or misusing here? I find it hard to believe that such simple behavior on the Ethereum blockchain couldn’t be replicated on Solana — especially given the great lengths the community has gone to to make Solana programming accessible through Solidity. If there’s something I’m doing wrong with this code I’d love to learn. Thank you so much in advance.
Edit: After upgrading my solang version, the first error was fixed. However, I'm now getting another error:
Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: instruction changed the balance of a read-only account
I'm not sure which account is supposedly read-only, as it isn't listed in the error response, but I'm pretty sure the only read-only account involved is the program as it's executable. How can I avoid this error?
The error Attempt to debit an account but found no record of a prior credit happens when you attempt to airdrop more than 1 SOL. If you wish to have more than 1 SOL, then airdrop 1 SOL in a loop until you have enough.

external service result mutates state of aggregate

My problem is that I don't know how to handle external calls that mutates the state but also needs validation before executing them
Here is my command handler
public async Task<IAggregateRoot> ExecuteAsync(Command command)
{
var sandbox = await _aggregateStore.GetByIdAsync<Sandbox>(command.SandboxId);
var response = await _azureService.CreateRedisInstance(sandbox.Id);
if (response.IsSuccess)
{
sandbox.CreateRedisDetails(response);
return sandbox;
}
sandbox.FailSetup(response.Errors.Select(e => e.Message));
return sandbox;
}
The problem here is that the sandbox aggregate needs to be in correct state before calling external service and I cannot satisfy both. My only idea here is to create separate method CanCreateRedisInstance that checks if aggregate state is valid and only then calls external service. What I don't like is that I introduce validation methods
public async Task<IAggregateRoot> ExecuteAsync(Command command)
{
var sandbox = await _aggregateStore.GetByIdAsync<Sandbox>(command.SandboxId);
if(!sandbox.CanCreateRedisInstance())
{
throw new ValidationExcetpion("something");
}
var response = await _azureService.CreateRedisInstance(sandbox.Id);
if (response.IsSuccess)
{
sandbox.CreateRedisDetails(response);
return sandbox;
}
sandbox.FailSetup(response.Errors.Select(e => e.Message));
return sandbox;
}
The other approach I thought of is to make whole process more cqrs-ish.
public async Task<IAggregateRoot> ExecuteAsync(Command command)
{
var sandbox = await _aggregateStore.GetByIdAsync<Sandbox>(command.SandboxId);
sandbox.ScheduleRedisInstanceCreation();
}
public void ScheduleRedisInstanceCreation()
{
if(RedisInstanceDetails != null)
{
throw new ValidationException("something")
}
RedisInstanceDetails = RedisInstanceDetails.Scheduled(some arguments);
AddEvent(new RedisInstanceCreationScheduled(some arguments));
}
The RedisInstanceCreationScheduled event is sent to queue and picked by event handler
which will call external service and based on result will create other events
public async Task ExecuteAsync(RedisInstanceCreationScheduled event)
{
var sandbox = await _aggregateStore.GetByIdAsync<Sandbox>(command.SandboxId);
var response = await _azureService.CreateRedisInstance(sandbox.Id);
if (response.IsSuccess)
{
sandbox.CreateRedisDetails(response);
return sandbox;
}
sandbox.FailSetup(response.Errors.Select(e => e.Message));
_aggregateStore.Save(sandbox);
}
However this approach add some extra complexity and I am not quite sure if event handler should modify aggregate.
Both approaches are possible.
Why no validation should stay in the Handler? When you change something in the domain, the domain object makes also a validation about the action, and deny it if it's not possible. Here you just need to interact with an external service to verify it.
The external service is just an interface in the domain layer, that you're going to implement with a concrete class into the infrastructure layer. Hence you will not have a directly binding with azure, but a service, let's say CloudService, that in it's implementation uses Azure. This allows you to build domain related exceptions that are thrown by classes that stay in the infrastructure layer.
Also the CQRS approach is valid. But you have to take care when you use it.
You can, for example, start a saga where you ask to the external service to create the instance (CreateRedisInstance), then, according to the event that you get (success or failure) you proceed with the next handler. But you really have to take care about middle situations: what should be done to handle failures between the 2 actions? You need also a rollback of the first action if the second one ends with a failure.
Said this, I would go with the first one if there're no really need to handle a complex process. Moreover, it looks that is all related to the same domain (no infra-domain actions are required), hence there're no real need to augment the complexity with a saga where every success/fail status should be correctly handled.

Lost ability to capture unique Conversation_ID for each new session

Using Bot Builder 4.11.1 .Net and seemed to have lost the ability to capture any unique identifier for each new session. I need a unique identifier to keep state with the AI engine I am using to respond to input. Any suggestions? So, to expand, if I have a slack bot, for example, each time a user logs into Slack and starts a conversation with MyBot, I need a new unique identifier.
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var welcomeText = "Hello and welcome!";
Random rnd1 = new Random();
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken);
}
}
}
}
Unless I'm missing something, you should be able to get the information you need from TurnContext. In my code I'm running this in onMessage only (as in my case I only will message the user if they have sent the bot a message), but I don't see why you couldn't use this in onMembersAdded as well. I don't know how channels like Slack work, but in Microsoft Teams the user is just "added" when they first talk to the bot, and you don't end up in onMembersAdded unless they remove and read the bot in Teams. So if you may want to grab the conversation reference in the future, you may want to have it in onMessage or in both places. Also if you need the activity ID for some reason, as this will obviously update with each activity (though I haven't had any need for this information). Here is how you get the conversation reference. I am storing this in my conversation state and have assumed you are familiar with that but let me know if you need further help there. I also store in an Azure table to be accessed outside of the bot (e.g. I have an Azure Function that uses this to send proactive followups).
const { TurnContext } = require('botbuilder');
const conversationData = await this.dialogState.get(context, {});
conversationData.conversationReference = TurnContext.getConversationReference(context.activity);
await this.conversationState.saveChanges(context);
And that's it! Here is a sample conversation reference. Note that if you are storing this in Azure Tables or similar, you'll likely need to stringify it and re-parse when you pull it out.
{
"activityId":"ACTIVITY_ID",
"user": {
"id":"USER_ID",
"name":"USER_NAME",
"aadObjectId":"AAD_OBJECT_ID",
"role":"user"
},
"bot": {
"id":"BOT_ID",
"name":"BOT_NAME"
},
"conversation": {
"conversationType":"personal",
"tenantId":"YOUR_TENANT_ID",
"id":"YOUR_CONVERSATION_ID"
},
"channelId":"msteams",
"locale":"en-US",
"serviceUrl":"https://smba.trafficmanager.net/amer/"
}
What you are looking for (I think) is conversationReference.conversation.id. Different channels are going to have different attributes in the conversation reference, but this ID should always be there.

Twilio Functions - SMS masking

Hello I am quite new to Twilio, but I have tried to look up how to answer this question. I would like to use Twilio Functions to solve my problem. I was wondering if it is possible for two people to send SMS messages to each other without revealing either of their numbers.
I was hoping to do this with only one new number per pair.
I imagined it would be through a conditional statement, where person X sends a message to the twilio number and person Y receives it, and vice versa. I assume this cannot be done with the twiML bins because of this conditional statement.
Thanks for your attention.
Twilio developer evangelist here.
You could absolutely do this with Twilio Functions. Here's a simple example of using a number to mask SMS messages between two callers.
class NumberMapping {
constructor() {
this.mapping = {};
}
addMaskedPair(numberA, numberB, twilioNumber) {
if (!this.mapping[twilioNumber]) {
this.mapping[twilioNumber] = {};
}
this.mapping[twilioNumber][numberA] = numberB;
this.mapping[twilioNumber][numberB] = numberA;
}
findNumber(from, to) {
const numberPairs = this.mapping[to];
if (!numberPairs) { return undefined; }
return numberPairs[from];
}
}
const numberMapping = new NumberMapping();
numberMapping.addMaskedPair('+1234567890', '+1098765432', '+1203948576');
exports.handler = function(context, event, callback) {
const to = numberMapping.findNumber(event.From, event.To);
if (typeof to !== 'undefined') {
const response = new Twilio.twiml.MessagingResponse();
response.message({ from: event.To, to: to }, event.Body);
callback(null, response);
} else {
callback(new Error(`Number mapping couldn't be found for sender ${event.From} and Twilio number ${event.To}.`));
}
};
The idea is that you create a NumberMapping object that maps between the two external numbers and your Twilio number. You add your mappings using:
numberMapping.addMaskedPair(firstNumber, secondNumber, twilioNumber);
and then when you need to retrieve the other number in a pair you can call
numberMapping.findNumber(number, twilioNumber);
The rest is just the function to return TwiML.
Note, you will only need as many Twilio numbers as there are relationships of the number that has the maximum set of relationships.
Let me know if that helps at all.
You need to purchase a number from twilio, then use node JS code to send and receive sms with it. You can also send voice messages too. The thing with twilio is that when you receive messages, twilio saves it to its website so you have to go to website and check it explicitly with your account.
You can create account and receive messages with this link
Here is some tutorial on how to send messages, you have to choose node.JS option.

How to share dynamic objects across workers?

I'm trying to make a game, which works on rooms, lobby and such (imagine the chat app, except with additional checks/information storing).
Let's say, I have a module room.js
var EventEmitter = require('events');
class Room extends EventEmitter {
constructor (id, name) {
super();
this.id = id;
this.name = name;
this.users = [];
}
}
Room.prototype.addUser = function (user) {
if(this.users.indexOf(user) === -1) {
this.users.push(user);
this.emit('user_joined', user);
} else {
/* error handling */
}
};
module.exports = {
Room: Room,
byId: function (id) {
// where should I look up?
}
};
How can I get exactly this object (with events)? How can I access events emitted by this object?
In a single instance of node, I would do something like:
var rooms = [];
var room = new Room(1234, 'test room');
room.on('user_joined', console.log);
rooms.push(room);
Also, I don't quite understood how Redis is actually helping (is it replacement of EventEmitter?)
Regards.
EDIT: Would accept PM2 solutions too.
Instead of handling rooms in Node, you can replace them with channels in Redis).
When a new client wants to join in a room, the NodeJS app returns it the ID of this given room (that is to say the name of the channel), then the client suscribes to the selected room (your client is directly connected to Redis.
You can use a Redis Set to manage the list of rooms.
In this scenario, you don't need any event emitter, and your node servers are stateless.
Otherwise, it would mean Redis would be exposed on the Internet (assuming your game is public), so you must activate Redis authentication. A major problem with this solution is that you have to give the server password to all clients, so it's definitely unsecure.
Moreover, Redis' performances allow brute force attacks so exposing it on Internet is not recommended. That's why I think all communications should go through a Node instance, even if Redis is used as a backend.
To solve this, you can use socket.io to open sockets between Node and your clients, and make the Node instances (not the client) subscribe to the Redis channel. When a message is published by Redis, send it to the client through the socket. And add a layer of authentication to ensure only valid clients connect to a given channel.
Event emitter is not required. It's the Redis client which will be an event emitter (like in this example based on ioRedis)

Resources