How do I handle an error with account subscribe? - solana

When an account is closed (on-chain), the account.subscribe listener try to decode the account, and it throws an error. How can I handle the error, to execute a callback when the error happens. It's an expected event.
Currently, I'm getting: "Error: Invalid account discriminator". Probably because the account doesn't exist anymore.
The account.subscribe method uses Solana Web3's onAccountChange and EventEmitter.

The account.subscribe listener decodes the account whenever an update for it is received:
const account = this._coder.accounts.decode(this._idlAccount.name, acc.data);
If the account is deleted, then it will fail to decode as the expected type. It looks like the code you linked could use a PR to fix this behavior!
In other places, it returns null if the account data is incorrect, so perhaps the event emitter should return null if the account fails to decode.
In the meantime, you can implement your own version of this which adds that check, ie:
const listener = this._provider.connection.onAccountChange(
address,
(acc) => {
if (acc == null) {
ee.emit("change", null);
} else {
const account = this._coder.accounts.decode(
this._idlAccount.name,
acc.data
);
ee.emit("change", account);
}
},
commitment
);

Related

How to get the result of a payable transaction using near-api-js?

When calling a contract method with attached deposits, you are redirected to the NEAR wallet for approving the transaction. How can the contract frontend app get the result of the transaction after returning from the wallet?
I've faced the same problem. For this moment near-api set transaction info in the browser url. So you get the transaction hash from url after returning from the wallet. Then using transaction hash get info about it using near-api-js:
const { providers } = require("near-api-js");
//network config (replace testnet with mainnet or betanet)
const provider = new providers.JsonRpcProvider(
"https://archival-rpc.testnet.near.org"
);
const TX_HASH = "9av2U6cova7LZPA9NPij6CTUrpBbgPG6LKVkyhcCqtk3";
// account ID associated with the transaction
const ACCOUNT_ID = "sender.testnet";
getState(TX_HASH, ACCOUNT_ID);
async function getState(txHash, accountId) {
const result = await provider.txStatus(txHash, accountId);
console.log("Result: ", result);
}
Documentation: https://docs.near.org/docs/api/naj-cookbook#get-transaction-status
There are 2 options:
Use provider.txStatus like Tom Links said. But the cons : we only know transaction success or fail but not the response from smart contract.
Seperate deposit api and actions api -> User must deposit before call actions api, so we can read the response.

how to define BusinessNetworkConnection in hyperledger-composer transaction processor?

In my hyperledger-composer app, I have a transaction processor:
async function doSomething(transaction) {
//some code
// the following line results in error message:
const connection = new BusinessNetworkConnection();
await connection.connect('admin#tmy-network');
const result = await connection.query(selectPatientByEmail, { inputValue: email });
//some more code
}
However, the line
const connection = new BusinessNetworkConnection();
causes the following error messsage:
ReferenceError: BusinessNetworkConnection is not defined
How can I define the BusinessNetworkConnection?
*******************************UPDATE**************************************
Following the comment by Paul O'Mahony, I used the following line of code in my transaction processor function (in order to get the patient with the email address 'adam#gmail.com'):
let result = await query('selectPatientByEmail', {
"email": "adam#gmail.com"
});
The query is defined in the queries.qry file as follows:
query selectPatientByEmail {
description: "Select the patient with the given email address"
statement:
SELECT org.comp.app.Patient
WHERE (email == _$email)
}
However, the query returns "undefined" (i.e. variable "result" is undefined) .
What for god's sake is wrong with the code? I just can't see what might be the causing this behaviour.
***************************Update2*****************************************
I have to correct myself ... the query returns something ... but when I want to access the id of the returned patient, this is not possible. That is,
result.id is "undefined"
How can I access the id of the patient returned?
this is because you are (above) writing client code inside a native transaction function - you don't need to set these. Transaction processor functions are automatically invoked by the runtime when transactions are submitted (eg using the BusinessNetworkConnection API under the covers, but it is already part of the transaction - you don't need to specify) . See https://hyperledger.github.io/composer/latest/reference/js_scripts for more info - and the sample networks for common use cases and examples -> https://github.com/hyperledger/composer-sample-networks/tree/master/packages/

Parse Cloud Code beforeSave timeout

When adding an email address to a list I'd like to check in a beforeSave function in my cloud code if the address belong to an existing user. If it doesn't I'd like to stop the save call and return an error response to my mobile app.
When I run the below code I have no problem while entering a valid email address. As soon as I enter an invalid address however the beforeSave function goes into a tizzy and times out after some time, returning a load of rubbish to the the client.
Parse.Cloud.beforeSave("EventUsers", function(request, response) {
var email = request.object.get("email");
console.log("starting beforeSave for user: " + email);
Parse.Cloud.useMasterKey();
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo("email", email);
userQuery.first().then(function(user) {
console.log("user: " + user.get("email"));
if (user) {
console.log("User exists");
response.success();
}
console.error("No user with that email");
response.error("199");
}, function(error) {
console.error(error);
response.error("198");
});
});
When I run this with an invalid email address I only get the very first console.log calls reported to my console - none of the others are showing.
I'm running my parse server on Heroku.
Are you running this on parse.com or on your own mongo db backend?
In any event, your problem is that email is likely not indexed so it is doing a full table scan. If it is your own backend, you can put an index on email in the collection.
If you're running your own db, not sure if anyone has done it yet (I haven't yet, but should), but you can probably also just put a unique constraint on email and then you can simply catch the rejected promise on user.save() and not worry about the beforeSave hook at all.

How can I correct an invalid key type in Parse Cloud Code?

We made a mistake in our client code where some users attempt to save a false boolean flag (by mistake) and others save a string to the same key in our Parse database.
I'm getting error code from Parse from everyone setting the false boolean as follows:
{"code":111,"error":"invalid type for key premiumType, expected string, but got boolean"}
Until we can release the next version of our client code, I want to intercept and correct this error IN CLOUD CODE. I'm trying this:
Parse.Cloud.beforeSave('GameScore', function(request, response) {
console.log("Entered function");
var premiumType = request.object.get("premiumType");
if (premiumType) {
request.object.set("premiumType", null);
response.success();
} else {
response.success();
}
});
But, the beforeSave function does not get entered unless the key type is correct, so I cannot modify the object here.
Are there any other locations where I can intercept this and modify the code in the cloud?

WCF Data Service - Add Object With Related Object

I have the following situation, I have a WCF Data Service with User objects and message objects and the message object has two relations to user, a sender and a receiver.
When I try to add a new Message object the related users are left null
Message message = new Message();
message.text = InputText; // string
message.Sender = Sender; // User object
message.Receiver = Receiver; // User object
context.AddToMessages(message);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
Now the Sender and Receiver will be null. When I try to set a link before the BeginSaceChanges like this I get the error "InvalidOperationException: The context is not currently tracking the entity."
context.AddToMessages(message);
context.AddLink(message, "Sender", message.Sender);
context.AddLink(message, "Receiver", message.Receiver);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
How do I make sure the relations are created properly?
Thanks to Pratik I found the solution. I had to use attach the already existing users Sender and Receiver to the context first because they weren't tracked (and added a if if they are on the second call). Then I add the message and use SetLink to set the link to both users (instead of AddLink)
if(context.GetEntityDescriptor(message.Sender) == null)
context.AttachTo("Users", message.Sender);
if (context.GetEntityDescriptor(message.Receiver) == null)
context.AttachTo("Users", message.Receiver);
context.AddToMessages(message);
context.SetLink(message, "Sender", message.Sender);
context.SetLink(message, "Receiver", message.Receiver);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
I believe you need to use the DbSet.Attach method instead. I assume you use Entity Framework on the back end here.

Resources