can't update state in solidity test blockchain - ethereum

I'm trying to connect this example to a frontend using web3. I have it copied exactly in my truffle project /contracts folder.
Compilation and migration are both successful. (I have truffle configured to deploy on my ganache test network, and when I run truffle migrate, I see that the first account is debited ether).
I've created a Vue app to interact with the contract via web3. Here's what my component looks like:
<template>
<div class="hello">
<button #click="inc">increment</button>
<button #click="dec">decrement</button>
<button #click="get">get</button>
</div>
</template>
<script>
const Web3 = require("web3");
const addr = '0xfbe7892aF06c7ecfbd83a0aD7F1D0a228d90Bf89'
const abi = [
{
"inputs": [],
"name": "count",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
},
{
"inputs": [],
"name": "get",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
},
{
"inputs": [],
"name": "inc",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "dec",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
export default {
name: 'HelloWorld',
data () {
return {
web3: null,
contract: null,
}
},
methods: {
get(){
self.contract.methods.get().call(function(err, result){
console.log(result)
})
},
inc(){
self.contract.methods.inc().call(function(err, result){
console.log(result)
})
},
dec(){
self.contract.methods.dec().call(function(err, result){
console.log(result)
})
},
},
created () {
self.web3 = new Web3('http://localhost:7545')
self.contract = new self.web3.eth.Contract(abi, addr)
}
}
</script>
When I run inc, no err is thrown. I expect to see that value increment when I run get, but it always outputs 0 - apparently inc doesn't update the state. Am I missing something?

There's a difference between a call (read-only, gas-free) and a transaction (read-write, costs gas fees).
Since you want the inc() function to update the state, you need to send() a transaction instead of a call.
Your local web3 instance (usually in production) or the remote node (usually in development, e.g. using Ganache) needs to hold the private key to the senderAddress to be able to send the transaction.
self.contract.methods.inc().send({from: senderAddress}, function(err, result){
console.log(result)
})

Related

FeathersJS Not Authenticated 401 Error when trying to use Access Token

In postman I am able to log in the user and get an accessToken:
Doing Post at http://localhost:3030/authentication I type in:
{
"strategy": "local",
"email": "bob#bob.com",
"password": "bob"
}
and then I get:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2VzcyJ9.eyJpYXQiOjE2NjEzMjgzMDEsImV4cCI6MTY2MTQxNDcwMSwiYXVkIjoiaHR0cHM6Ly95b3VyZG9tYWluLmNvbSIsImlzcyI6ImZlYXRoZXJzIiwic3ViIjoiMSIsImp0aSI6IjMyOWMwZTU3LTM5NTctNDUxOS05N2ZmLTRiNDIxOWI2MDQ2YSJ9.tIiRCMqzNg8F4lb1tzfYrOVvc148qRmZrZ7FPouHhKg",
"authentication": {
"strategy": "local",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2VzcyJ9.eyJpYXQiOjE2NjEzMjgzMDEsImV4cCI6MTY2MTQxNDcwMSwiYXVkIjoiaHR0cHM6Ly95b3VyZG9tYWluLmNvbSIsImlzcyI6ImZlYXRoZXJzIiwic3ViIjoiMSIsImp0aSI6IjMyOWMwZTU3LTM5NTctNDUxOS05N2ZmLTRiNDIxOWI2MDQ2YSJ9.tIiRCMqzNg8F4lb1tzfYrOVvc148qRmZrZ7FPouHhKg",
"payload": {
"iat": 1661328301,
"exp": 1661414701,
"aud": "https://yourdomain.com",
"iss": "feathers",
"sub": "1",
"jti": "329c0e57-3957-4519-97ff-4b4219b6046a"
}
},
"user": {
"id": 1,
"email": "bob#bob.com",
"createdAt": "2022-08-24T08:04:57.464Z",
"updatedAt": "2022-08-24T08:04:57.464Z"
}
}
But then when I try to use accessToken for another POST in Postman, I get:
{
"name": "NotAuthenticated",
"message": "Not authenticated",
"code": 401,
"className": "not-authenticated",
"errors": {}
}
Why is this?
One idea I have is maybe I did this part wrong. I thought I was supposed to add
auth.associateCurrentUser({
idField: "id",
as: "group_admin",
}),
But I was getting an error & found something online that said to change it to this (context below):
setField({
from: "params.user.id",
as: "data.group_admin",
}),
import * as authentication from "#feathersjs/authentication";
// Don't remove this comment. It's needed to format import lines nicely.
const { authenticate } = authentication.hooks;
import { setField } from "feathers-authentication-hooks";
export default {
before: {
all: [authenticate("jwt")],
find: [],
get: [],
create: [
setField({
from: "params.user.id",
as: "data.group_admin",
}),
// auth.associateCurrentUser({
// idField: "id",
// as: "group_admin",
// }),
],
update: [],
patch: [],
remove: [],
},
Do you have any ideas to get the accessToken to work with authentication?
I can post more code if needed.

View functions error : Returned values aren't valid, did it run Out of Gas?

I'm calling the methods in the deployed smart contracts in Ganache. Other methods are working fine, but the view functions returns an error.
Here's the view function in Solidity:
mapping (address => Count) private counts;
function getCounts (address user)
public
view
returns(uint a, uint b, uint total){
return(counts[user].a, counts[user].b, counts[user].total);
}
Here's how I call the method:
web3.eth.getAccounts(function(error, accounts){
if(error){
console.log(error);
}
var account = accounts[0];
contracts.SampleContract.deployed().then(function(instance){
credsInstance = instance;
return credsInstance.getCounts.call(account, {from: account});
}).then(function(creds){
console.log(creds[0]);
}).catch(function(error){
console.log(error.message);
});
});
Here's the ABI of getCounts
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
}
],
"name": "getCounts",
"outputs": [
{
"internalType": "uint256",
"name": "academic",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "workExp",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "total",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
},
I'm getting this error:
Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced.
I also have the latest web3. How can I fix this?
Edit:
to initialize the contract I used Truffle's official documentation. Here's the code:
var contract = require('#truffle/contract');
$.getJSON('../build/contracts/SampleContract.json', function(data) {
// Get the necessary contract artifact file and instantiate it with #truffle/contract
var CredsArtifact = data;
contracts.SampleContract = TruffleContract(CredsArtifact);
// Set the provider for our contract
contracts.SampleContract.setProvider(web3Provider);
});
I had the same problem, and I was using Hardhat .
after spending hours and not resolved, I switched Rinkeby test network, and everything goes well.
refer to: https://ethereum.stackexchange.com/a/129721/30431

Web Application doesn't communicate correctly with Ethereum smart contract deployed through Truffle

I coded this really simple smart contract in Solidity which allows users to add todo tasks to their personal list, to fetch their list of todos, and so on.
pragma solidity ^0.8.0;
contract ToDo {
struct Task {
string content;
bool completed;
}
mapping(address => Task[]) private tasks;
function addTask(string memory content) public {
tasks[msg.sender].push(Task(content, false));
}
function changeTaskState(uint256 taskId) public {
tasks[msg.sender][taskId].completed = !tasks[msg.sender][taskId].completed;
}
function editTaskContent(uint256 taskId, string memory content) public {
tasks[msg.sender][taskId].content = content;
}
function getTasks() public view returns(Task[] memory) {
return tasks[msg.sender];
}
}
This works exactly as intended when deployed through Truffle and tested in the Truffle(develop) terminal:
truffle(develop)> const todo = await ToDo.deployed()
undefined
truffle(develop)> todo.getTasks()
[]
truffle(develop)> todo.addTask("Hello, world!")
{
tx: '0x7e607352c1ab8f6532c5b43e282eb20f29d5bfa451dfbb873bac3506df00cb1a',
receipt: {
transactionHash: '0x7e607352c1ab8f6532c5b43e282eb20f29d5bfa451dfbb873bac3506df00cb1a',
transactionIndex: 0,
blockHash: '0x98b361190eadf1905c3e15b5054aa4ace8eaa33a2b4d35898f78e2165ea996a2',
blockNumber: 5,
from: '0x3455100c0b0617afbf0f53db5e5c07366e20791b',
to: '0x645a78fe8eb3529291ba63a8e420d26c7baf61a0',
gasUsed: 66634,
cumulativeGasUsed: 66634,
contractAddress: null,
logs: [],
status: true,
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
rawLogs: []
},
logs: []
}
truffle(develop)> todo.changeTaskState(0)
{
tx: '0xddb313978411cd3f1429f1eb61b9bbde816e3a874d765aa5588a69508d226b44',
receipt: {
transactionHash: '0xddb313978411cd3f1429f1eb61b9bbde816e3a874d765aa5588a69508d226b44',
transactionIndex: 0,
blockHash: '0xbae43abf22ca06de65a41e3e54493ad944f4b997b12a3ed407bc5f778d00a3be',
blockNumber: 6,
from: '0x3455100c0b0617afbf0f53db5e5c07366e20791b',
to: '0x645a78fe8eb3529291ba63a8e420d26c7baf61a0',
gasUsed: 45320,
cumulativeGasUsed: 45320,
contractAddress: null,
logs: [],
status: true,
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
rawLogs: []
},
logs: []
}
truffle(develop)> todo.getTasks()
[
[ 'Hello, world!', true, content: 'Hello, world!', completed: true ]
]
However, when I try to call these contract's functions from a web-app, it seems like there are some kind of communication errors with the local blockchain provided by Truffle.
Of course I've installed Metamask in my browser and I've connected it to http://127.0.0.1:9545 (as Truffle tells me to do upon executing the truffle develop command). I've also imported the private phrase provided by Truffle, so that I could access the 10 test addresses on that local network.
I've also found the contract's address and ABI in the build/contracts directory and set up a simple front end in React.
import Web3 from 'web3';
import React, { useState, useEffect } from "react";
const TODO_ABI =
[
{
"inputs": [
{
"internalType": "string",
"name": "content",
"type": "string"
}
],
"name": "addTask",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "taskId",
"type": "uint256"
}
],
"name": "changeTaskState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "taskId",
"type": "uint256"
},
{
"internalType": "string",
"name": "content",
"type": "string"
}
],
"name": "editTaskContent",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getTasks",
"outputs": [
{
"components": [
{
"internalType": "string",
"name": "content",
"type": "string"
},
{
"internalType": "bool",
"name": "completed",
"type": "bool"
}
],
"internalType": "struct ToDo.Task[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function",
"constant": true
}
];
const TODO_ADDRESS = "0x645a78fe8eb3529291ba63a8e420d26c7baf61a0";
function ChangeTaskStateButton(props) {
return (
<button onClick={ () => props.contract.methods.changeTaskState(props.id).call() }>{ props.state }</button>
);
}
function Task(props) {
return (
<li>
{ props.content } | <ChangeTaskStateButton contract={ props.contract } id={ props.id } state={ props.completed ? "Completed" : "Pending "}></ChangeTaskStateButton>
</li>
);
}
function TasksList(props) {
let tasks = [];
const tasksData = props.tasks;
for(let i = 0; i < tasksData.length; i++) {
tasks.push(<Task id={i} content={ tasksData[i].content } completed={ tasksData[i].completed } contract={ props.contract }></Task>);
}
return (
<div>
<ul>
{ tasks }
</ul>
</div>
);
}
function TaskForm(props) {
const [content, setContent] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
props.contract.methods.addTask(content).call()
.then(() => props.setTasks(props.tasks.concat({content: content, completed: false})));
};
const handleChange = (event) => {
setContent(event.target.value);
};
return(
<form onSubmit={ handleSubmit }>
<input type="text" onChange={ handleChange }></input>
<button type="submit">Submit</button>
</form>
);
}
function App() {
const [web3] = useState(new Web3(Web3.givenProvider || "http://localhost:9545"));
const [contract] = useState(new web3.eth.Contract(TODO_ABI, TODO_ADDRESS));
const [tasks, setTasks] = useState([]);
useEffect(() => {
contract.methods.getTasks().call()
.then(tasks => {
setTasks(tasks);
});
}, [contract.methods]);
return (
<div>
<TaskForm contract={contract} setTasks={setTasks} tasks={tasks}></TaskForm>
<TasksList tasks={tasks} contract={contract}></TasksList>
</div>
);
}
The call to getTasks() always returns an empty array, even if I add a task through the terminal with the same address that's currently in use on Metamask, while the call to addTask() doesn't store anything in the smart contracts's map. The call to these two functions don't cause any errors or warnings to appear in the browser's console. However, the call to changeTaskState() does cause two errors do be displayed:
inpage.js:1 MetaMask - RPC Error: Internal JSON-RPC error.
{code: -32603, message: "Internal JSON-RPC error.", data: {…}}
code: -32603
data: {message: "VM Exception while processing transaction: revert", code: -32000, data: {…}}
message: "Internal JSON-RPC error."
__proto__: Object
index.js:50 Uncaught (in promise) Error: Internal JSON-RPC error.
{
"message": "VM Exception while processing transaction: revert",
"code": -32000,
"data": {
"0x359c33ac64b2b3eb0096b40b2d225679d4212f40fc86ef938af49fcc47159f2c": {
"error": "revert",
"program_counter": 994,
"return": "0x4e487b710000000000000000000000000000000000000000000000000000000000000032"
},
"stack": "RuntimeError: VM Exception while processing transaction: revert\n at Function.RuntimeError.fromResults (C:\\Users\\gianm\\AppData\\Roaming\\npm\\node_modules\\truffle\\build\\webpack:\\node_modules\\ganache-core\\lib\\utils\\runtimeerror.js:94:1)\n at C:\\Users\\gianm\\AppData\\Roaming\\npm\\node_modules\\truffle\\build\\webpack:\\node_modules\\ganache-core\\lib\\blockchain_double.js:568:1",
"name": "RuntimeError"
}
}
at Object._fireError (index.js:50)
at sendTxCallback (index.js:540)
at cb (util.js:689)
at callbackifyOnRejected (util.js:666)
at Item.push../node_modules/process/browser.js.Item.run (browser.js:153)
at drainQueue (browser.js:123)
I've also tried to use Ganache, instead of Truffle's built-in local blockchain, and I even tried to change browser, but nothing seems to work. I've also checked whether Metamask was actually connected to the webapp and sure enough it was. What am I missing here?
There are two main ways to interact with a smart contract. A call (read-only, free) and a transaction (read-write, requires gas fees).
Your react code uses the .call() method for addTask() and changeTaskState(), which doesn't allow writing in the contract storage.
Since you're working with MetaMask, you should use the Ethereum Provider API and submit a request to MM that will ask the user to confirm the transaction.
So instead of the props.contract.methods.addTask(content).call() you can get the contents of the data field and then generate the transaction request.
const data = props.contract.methods.addTask(content).encodeABI();
ethereum.request({
method: 'eth_sendTransaction',
params: [{
from: senderAddress,
to: contractAddress,
data: data
}],
});
Note: You can set the senderAddress after connecting to MM.
Another way is to use the web3 .send() method, but this requires passing the private key of the sender to web3 (bad idea in a frontend JS app) or unlocked provider account (local providers usually have few unlocked accounts, but production ones don't).

Loopback : adding data in relation table of type "hasMany"

I have a table representing profil of users. This profils can follow others profils.
Here is a part of my profil.json
"relations": {
"followers": {
"type": "hasMany",
"model": "Profil",
"foreignKey": "publisherId",
"keyThrough": "subscriberId",
"through": "Subscribing"
},
"subscribings": {
"type": "hasMany",
"model": "Profil",
"foreignKey": "subscriberId",
"keyThrough": "publisherId",
"through": "Subscribing"
},
Well this works fine, but now I want to known the date when the profil was subscribed.
So I updated the subscribing.json relation table to add a date
{
"name": "Subscribing",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"subscriptionDate": {
"type": "date",
"required": true,
"default": "$now"
}
},
"validations": [],
"relations": {
"subscriber": {
"type": "belongsTo",
"model": "Profil",
"as": "subscriber"
},
"publisher": {
"type": "belongsTo",
"model": "Profil",
"as": "publisher"
}
},
"acls": [],
"methods": {}
}
Now I would like to be able to query the data of this profils while having the possibility to retrieve the date of subscription in the same time. Ideally, I would to keep the relation table non public.
Thanks
Try add subscribers manually.
First, add the next to subscribing.json
"scope": {
"include": [
"subscriber"
]
}
Next, create the hook observer in profil.js (I use lodash for mapping but you can use your prefer library)
Profil.observe('loaded', function(ctx, next){
const Subscribing = Profil.app.models.Subscribing;
Subscribing.find({
where: {
publisherId: ctx.data.id
}
}, function (err, subscribings) {
if(err) return next(err)
ctx.data.subscribings = _.map(subscribings, function(subscribing){
return {
date: subscribing.subscriptionDate,
subscriber: subscribing.subscriber
}
});
next();
});
})
And it's all! Try this and comment if you have problems.
You can use the "include" filter to add the relation data to the response.
So in your case this should get a user's 'subscribings':
Profil.find({include: 'subscribings'}, function() { /* ... */ });
One way is mentioned by #rigobcastro in his answer.
Alternatively, you need to create a new relation.
Let's say we call it SubscribingMetaData
In your profile.json, add following
"subscribingMetaData": {
"type": "hasMany",
"model": "Subscribing",
"foreignKey": "subscriberId"
}
Now you can include it in your query,
Profil.find({include: ['subscribings', 'subscribingMetaData']}, function() { /* ... */ });
// process subscribingMetaData and map subscriptionDate with subscribings to get desired output.

AWS Security Group Creation NodeJS

I have a script that creates a security group(s) in AWS, it creates rules for ingress (inbound) and egress (outbound) traffic, my script looks like this right now:
#!/usr/bin/env node
/*
This is a script to generate security groups and apply them to instances in a VPC.
Attached to this script is a json file which has the security group parameters in it.
Run this script by executing:
node AWS_Security_Groups.js
*/
'use strict';
process.env.AWS_PROFILE
var PropertiesReader = require('properties-reader');
var AWS = require('aws-sdk')
var properties = PropertiesReader('/Users/testuser/.aws/credentials');
AWS.config.update({
accessKeyId : properties.get('aws_access_key_id'),
secretAccessKey : properties.get('aws_secret_access_key'),
region : 'us-east-1'
})
var ec2 = new AWS.EC2({apiVersion: '2016-11-15'});
// Load credentials and set region from JSON file
//AWS.config.loadFromPath('/Users/testuser/.aws/credentials');
// Load in security group parameters
let securityParams = require('./securityParams.json');
module.exports = {
//Exports creation of Security Groups
createSecurityGroup: (req, res) => {
ec2.createSecurityGroup(securityParams, function(err, data) {
if (err) {
return (console.log("Error", err));
}
// Pass the Json as a parameter in this function
ec2.authorizeSecurityGroupIngress(securityParams, function(err, data) {
if (err) {
res.serverError(err, err.stack);
} else {
res.ok(data);
console.log('Ingress Security Rules Created');
}
})
// Pass the Json as a parameter in this function
ec2.authorizeSecurityGroupEgress(securityParams, function(err, data) {
if (err) {
res.serverError(err, err.stack);
} else {
res.ok(data);
console.log('Egress Security Rules Created');
}
})
})
}
}
module.exports.createSecurityGroup();
My Json file looks like this:
{
"SecurityGroups": [
{
"IpPermissionsEgress": [],
"Description": "My security group",
"IpPermissions": [
{
"PrefixListIds": [],
"FromPort": 22,
"IpRanges": [
{
"CidrIp": "203.0.113.0/24"
}
],
"ToPort": 22,
"IpProtocol": "tcp",
"UserIdGroupPairs": []
}
],
"GroupName": "MySecurityGroup",
"OwnerId": "123456789012",
"GroupId": "sg-903004f8",
}
{
"IpPermissionsEgress": [],
"Description": "My security group2",
"IpPermissions": [
{
"PrefixListIds": [],
"FromPort": 22,
"IpRanges": [
{
"CidrIp": "203.0.113.0/24"
}
],
"ToPort": 22,
"IpProtocol": "tcp",
"UserIdGroupPairs": []
}
],
"GroupName": "MySecurityGroup2",
"OwnerId": "123456789012",
"GroupId": "sg-903004f28",
}]
}
However I can't get the script to execute properly. I keep getting an error saying unread character '/' in the JSON file. Does anyone know what I'm missing? Also, I want to be able update the script to read in the security groups and if the group already exists don't try and create it.
So this JSON seems to work to some extent: It doesn't create any rules, just creates the security group:
[
{
"IpProtocol": "string",
"FromPort": integer,
"ToPort": integer,
"UserIdGroupPairs": [
{
"UserId": "string",
"GroupName": "string",
"GroupId": "string",
"VpcId": "string",
"VpcPeeringConnectionId": "string",
"PeeringStatus": "string"
}
...
],
"IpRanges": [
{
"CidrIp": "string"
}
...
],
"Ipv6Ranges": [
{
"CidrIpv6": "string"
}
...
],
"PrefixListIds": [
{
"PrefixListId": "string"
}
...
]
}
...
]
I have to update my script with the params: securityParams[0].UserIdGroupPairs[0]