Why is there an error in this contract call? - nearprotocol

new to nearprotocol! Trying to do a little hello world using near-api-js, here is my issue ...
const { keyStores, connect } = require("near-api-js");
const fs = require("fs");
const path = require("path");
const homedir = require("os").homedir();
const CREDENTIALS_DIR = ".near-credentials";
const ACCOUNT_ID = "acct.testnet";
const WASM_PATH = "./contract/target/wasm32-unknown-unknown/release/counter.wasm";
const credentialsPath = path.join(homedir, CREDENTIALS_DIR);
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath);
const config = {
keyStore,
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
deployContract(ACCOUNT_ID, WASM_PATH);
async function deployContract(accountId, wasmPath) {
const near = await connect(config);
const account = await near.account(accountId);
const result = await account.deployContract(fs.readFileSync(wasmPath));
}
I am deploying a wasm contract with this methodology, however, when I try to call the contract using
const nearAPI = require("near-api-js");
const keyStores = nearAPI.keyStores;
const connect = nearAPI.connect;
const homedir = require("os").homedir();
const CREDENTIALS_DIR = ".near-credentials";
const credentialsPath = require("path").join(homedir, CREDENTIALS_DIR);
const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath);
const config = {
networkId: "testnet",
keyStore,
nodeUrl: "https://rpc.testnet.near.org",
walletUrl: "https://wallet.testnet.near.org",
helperUrl: "https://helper.testnet.near.org",
explorerUrl: "https://explorer.testnet.near.org",
};
call();
async function call() {
// gets the state of the account
const near = await connect(config);
const account = await near.account("acct.testnet");
const contract = new nearAPI.Contract(
account, // the account object that is connecting
"acct.testnet",
{
contractID : "counter.acct.testnet",
changeMethods: ["increment", "decrement", "reset"], // view methods do not change state but usually return a value
viewMethods: ["get_num"], // change methods modify state
sender: account, // account object to initialize and sign transactions.
}
);
let response = await contract.reset(
{
args: {
//arg_name: "value" // argument name and value - pass empty object if no args required
},
gas: 300000000000000 // attached GAS (optional)
}
);
console.log(response);
}
Now, the response says that the contract does not exist: ServerTransactionError: Can't complete the action because account counter.acct.testnet doesn't exist.
However, when using acct.testnet, instead of counter.acct.testnet, it works.
Which leaves the question: how can I specify the exact contract that I want to interact with, on the near blockchain, under a specific account?
Thanks!

There are two different ways you can go about using NAJ (near-api-js) and interacting with contracts. The first way, which is the one you're using, is to create a contract object (new nearAPI.Contract()) and connect to the contract using an account object (either from a wallet connection or the native near.account() method):
const contract = new nearAPI.Contract(
accountObject, // the account object that is connecting
...
This method allows you to pass in the account ID of the contract you wish to interact with:
const contract = new nearAPI.Contract(
accountObject,
"CONTRACT_ACCOUNT_ID_HERE.testnet",
...
In your case, simply change that account ID to be whatever you want and that will allow you to interact with different contracts. These account IDs must exist and also have a contract deployed to them otherwise you'll run into errors. Don't forget that the methods you outline when creating the nearAPI contract object must also live on the contract or else when you try to use them, it will throw as well.
The second way you can interact with contracts, which is my preferred way of doing things, is to create the account object in the same manner as you did previously but instead of creating a contract object and doing contract.methodName, you just take the account object and do account.functionCall or account.viewFunction where you can pass in the contract ID, method name etc...
This allows you to interact with any contract on-chain without having to create a new contract object for each contract you want to interact with.

Related

Update a user record with a pointer using Parse Cloud Code

I have viewed all the articles on here, and haven't done any javascript coding in the past. Hoping someone can help me.
I have a class called rank and of course the parse _User class. I have added a pointer to the _User class to the rank class (the column name is called user_rank, which allows me to give a user a rank - seems simple enough.
What I am trying to achieve, is to use Cloud Code to change a user's rank as the administrator of the app (so it's something I do in my admin app, not the user does in their app).
This is what I have, but all I get is an error 101 'Object not found'. I have no doubt I am doing this all wrong, but I have tried to piece together responses from other posts with no success.
Any help is greatly appreciated.
Updated code with Davi's change below - now throwing error schema mismatch for _User.user_rank; expected Pointer but got String
Parse.Cloud.define("setUserRank", async (request, response) => {
let { userObjectId, rankObjectId } = request.params;
const userQuery = new Parse.Query(Parse.User);
const rankQuery = new Parse.Query('rank');
// Get the user object to change the rank of
try{
let user = await userQuery.get(userObjectId, { useMasterKey: true});
let rank = await rankQuery.get(rankObjectId, { useMasterKey: true});
console.log(user);
console.log("Running");
const rankRelation = user.relation('user_rank');
rankRelation.add(user_rank);
user.save(null, { useMasterKey: true});
return ("User Rank Changed"));
} catch (err) {
throw new Error(err.message)
}
});
I think the problem happens because of this line:
const rankQuery = new Parse.Query(Parse.rank);
In the case of you custom classes, you need to pass the class name as a string:
const rankQuery = new Parse.Query('rank');

Microsoft Graph MVC how to force re-authentication

I need to force re-authentication of Microsoft Graph within an MVC Core application.
The Graph object is obtained in ConfigureServices using the code segment:
var tokenAcquisition = context.HttpContext.RequestServices
.GetRequiredService<ITokenAcquisition>();
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(async (request) => {
var token = await tokenAcquisition
.GetAccessTokenForUserAsync(_scopes, user: context.Principal);
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
})
);
The problem is the token goes stale and a later call to Graph fails. Easy to trap and to put in some reauthentication code except it also fails, with a "MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call" error. Plenty of reference to this scenario online but no definitive response that I can find.
Reauthentication code in the controller is:
if (ex.InnerException.InnerException is MsalUiRequiredException)
{
string[] _scopes = _config.GetValue<string>("AzureAd:GraphScopes")?.Split(' ');
var tokenAcquisition = _http.RequestServices.GetRequiredService<ITokenAcquisition>();
_graph = new GraphServiceClient(
new DelegateAuthenticationProvider(async (request) =>
{
var options = new TokenAcquisitionOptions() { ForceRefresh = true };
var token = await tokenAcquisition.GetAccessTokenForUserAsync(_scopes, user: User, tokenAcquisitionOptions: options);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
})
);
}
Question is how to successfully force reauthentication and obtain a new Graph client?
Answering my own question, turns out it's easily handled in the Controller:
try
{
string token = await _tokenAcquisition
.GetAccessTokenForUserAsync(GraphConstants.Scopes);
return View().WithInfo("Token acquired", token);
}
catch (MicrosoftIdentityWebChallengeUserException)
{
return Challenge();
}
This code segment is from https://learn.microsoft.com/en-us/graph/tutorials/aspnet-core?tutorial-step=3
I assume that your application is forcing the user to log in and you're using that identity to get a Graph token based on the use of context.Principal:
.GetAccessTokenForUserAsync(_scopes, user: context.Principal);
When the token expires I assume the original token that was used to get in has also expired about the same time. That means that there is no user and therefore calls fail with the error that you're describing. It makes me think you need to reauthenticate before you try to get a new graph token.
However, you should monitor the token and get a new one just before it expires - silently -using the refresh token rather than a new authentication.
https://learn.microsoft.com/en-us/advertising/guides/authentication-oauth-get-tokens?view=bingads-13#refresh-accesstoken

Function app not waiting for Promise returned from web3 contract.getpastevents

I am new to using function app as well as JS. Below is what i am trying to do :
we have a internal quorum cluster using Azure blockchain service. we have deployed a contract on it and need to check the events for this contract. And in general, want to interact with this contract.
For interacting with this contract, we plan to use a node js app which will be hosted using Azure Function app.
I have created a test script which creates contract instance and logs the output of getpastevents method.
The script is working fine on my local node setup but on function app, the execution does not wait for the promise to finish and capture the final result. Rather, it just logs 'promise pending' and moves on.
please ignore the myBlob input, i am just testing this function in a Blob trigger.
code :
module.exports = async function (context, myBlob) {
context.log("JavaScript blob trigger function processed blob \n Blob:", context.bindingData.blobTrigger, "\n Blob Size:", myBlob.length, "Bytes");
var Web3 = require('web3')
const rpcURL = "https://<myapp>.blockchain.azure.com:port/key"
const web3 = new Web3(rpcURL)
const account = <validAccount>
var privateKey = Buffer.from(<validkey>, 'hex')
var abi=,Validabi>
var contractAddress=<contractaddress>;
var contract = new web3.eth.Contract(abi, contractAddress);
contract.getPastEvents('allEvents', {fromBlock : 0, toBlock :'latest'}).then((events) => { result = events;
context.log(result)}).catch((error)=>{context.log('error')});
};
Got the answer through Quorum Slack channel. below is the working code:
module.exports = async function (context, myBlob) {
context.log("JavaScript blob trigger function processed blob \n Blob:", context.bindingData.blobTrigger, "\n Blob Size:", myBlob.length, "Bytes");
var Web3 = require('web3')
const rpcURL = "https://<myapp>.blockchain.azure.com:port/key"
const web3 = new Web3(rpcURL)
const account = <validAccount>
var privateKey = Buffer.from(<validkey>, 'hex')
var abi=,Validabi>
var contractAddress=<contractaddress>;
var contract = new web3.eth.Contract(abi, contractAddress);
let events = contract.getPastEvents('allEvents', {fromBlock : 0, toBlock :'latest'})
.catch((error)=>{context.log('error')});
context.log(events);
};

Mentioning a user in the System.History

I'm trying to add a new comment to a work item which mentions a user, but using the traditional "#adamh" as you would do on the website does not seem to work via the API.
The data updates fine, however the "#adamh" is just plain text, I need to be able to somehow chuck an identity into here. Can anyone point me in the right direction?
Thanks!
A snippet is here
const vsts = require('vso-node-api');
const item = require('vso-node-api/WorkItemTrackingApi')
const ti = require('vso-node-api/interfaces/WorkItemTrackingInterfaces');
// your collection url
const collectionUrl = "https://myArea.visualstudio.com/defaultcollection";
// ideally from config
const token = "helloWorld";
async function run() {
let authHandler = vsts.getPersonalAccessTokenHandler(token);
let connection = new vsts.WebApi(collectionUrl, authHandler);
let itemTracking = await connection.getWorkItemTrackingApi();
//Add all task data to new array
let taskData = await itemTracking.getWorkItems([15795,15796])
let newData = taskData[0]
let wijson = [
{
"op": "add",
"path": "/fields/System.History",
"value": "#adamh"
}
];
const updateItem = itemTracking.updateWorkItem(null, wijson, 15795).catch(err => {
console.log(err)
}).then(() => console.log("updated"))
return newData
}
const express = require('express')
const app = express()
app.get('/', async (req, res) => {
let data = await run()
res.send(data)
})
app.listen(3000, () => console.log('Example app listening on port 3000!'))
You can use the format shown here as part of the text value for your new comment:
...
This will create a mention link to that user. The link text can be the person's name or any other text you choose to put there. An email alert will be sent to the mentioned user if your system is configured to do so (same as in the UI).
To get your users' userid strings, you can follow the method shown here.
You can use the # to notify another team member about the discussion. Simply type # and their name.
It's using the #mention control , the person you #mention will receive an email alert with your comment and a link to the work item, commit, changeset, or shelveset.
There is not any public API shows how this work in VSTS, you could try to use F12 in google browser to track the process. Another workaround is directly using API to send a notification to the user you want to mention at.

Google AUTH API Application Type, how important is it?

I've been doing a lot tinkering around with the authentication stuff using the .NET libraries provided by Google.
We have both a desktop and web-app side, and what we want to achieve is to authenticate ONCE, either on the desktop or the web side, and store the refresh token, and reuse it both on the web side and the desktop side.
So the situation is like so, on the desktop side, when there's no saved existing AccessToken's and RefreshToken's, we will ask the user to authenticate via this code:
using (var stream = new FileStream("client_secrets_desktop.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
new[] { GmailService.Scope.GmailReadonly, GmailService.Scope.GmailCompose },
"someemail#gmail.com", CancellationToken.None);
}
In this case the Client ID and Secret is of an Application type Installed Application.
On the web-application side, if there's also no refresh token yet then I'm using DotNetOpenAuth to trigger the authentication, here's the code snippet:
const string clientID = "someclientid";
const string clientSecret = "somesecret";
const string redirectUri = "http://localhost/Home/oauth2callback";
AuthorizationServerDescription server = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"),
TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
ProtocolVersion = ProtocolVersion.V20
};
public ActionResult AuthenticateMe()
{
List<string> scope = new List<string>
{
GmailService.Scope.GmailCompose,
GmailService.Scope.GmailReadonly,
GmailService.Scope.GmailModify
};
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
// Here redirect to authorization site occurs
OutgoingWebResponse response = consumer.PrepareRequestUserAuthorization(
scope, new Uri(redirectUri));
response.Headers["Location"] += "&access_type=offline&approval_prompt=force";
return response.AsActionResult();
}
public void oauth2callback()
{
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
consumer.ClientCredentialApplicator =
ClientCredentialApplicator.PostParameter(clientSecret);
IAuthorizationState grantedAccess = consumer.ProcessUserAuthorization(null);
string accessToken = grantedAccess.AccessToken;
}
Here is where I want to confirm my suspicions.
When there is a RefreshToken that exists, we use the following code snippet to call the Gmail API's
UserCredential uc = new UserCredential(flow, "someemail#gmail.com", new TokenResponse()
{
AccessToken = "lastaccesstoken",
TokenType = "Bearer",
RefreshToken = "supersecretrefreshtoken"
});
var refreshState = await uc.RefreshTokenAsync(CancellationToken.None);
var svc = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = uc,
ApplicationName = "Gmail Test",
});
Here's the thing I noticed is that, for me to be able to use the refresh token to refresh from either the desktop or the web side, the refresh token needs to be generated through the same client ID/secret combination. I've tested it and it seems like it's fine if we use Installed application as the application type for the Client ID for both the desktop and the web, but my question I guess is, these application type's for the client IDs, do they matter so much?
Am I doing anything wrong to do it this way?
Thanks in advance

Resources