How to persist data after deployment in Solidity - blockchain

I am pretty new to Solidity and working with Ethereum in general.
In the (d)app I'm working on I need to be able to persist data onto the ledger, but I'm not sure I understand how this works.
Let's say I have the following contract (simplified for practicality):
contract UserContract {
struct User {
address walletAddress;
string organisation;
string fName;
string lName;
string email;
uint index;
}
mapping(address => User) private users;
address[] private userIndex;
function insertUser(
address walletAddress,
string organisation,
string fName,
string lName,
string email )
public
returns(uint index) {
User memory newUser = User({
walletAddress: walletAddress,
organisation: organisation,
fName: fName,
lName: lName,
email: email,
index: users.length
});
users.push(newUser);
userIndex[walletAddress] = newUser.index;
return newUser.index;
}
}
Using the insertUser() method, I can insert a new user, and using a getter method I could retrieve the user's information.
Now, if I update the contract (thus deploy a new one), the users mapping is empty again, not surprising.
My question: how do I store data in a way that it will be accessible for future versions of the contract? Any design patterns that go along with this process?
Thanks!

Since, as you know, stored data will not travel with a new contract and copying over data to a new contract would be expensive task after a few uses...your best bet is to separate functionality from data storage.
Create a contract with some basic setters and getters that only deals with data storage (from a particular contract address if necessary)...then create your main functional contract that connects to the data contract.

Related

BSC Development: Where to get dividendTracker adress for new BinanceSmartChain?

I am trying to mint new token. This is part of code. On the fifth line is definition of list of four adresses,
First is contract of code which will be reward, second is router (every DEX has unique one), third is marketing wallet, fourth is dividendTracker.
Where can I have dividendTracker adress?
Thank you
constructor(
string memory name_,
string memory symbol_,
uint256 totalSupply_,
address[4] memory addrs, // reward, router, marketing wallet, dividendTracker
uint256[3] memory feeSettings, // rewards, liquidity, marketing
uint256 minimumTokenBalanceForDividends_,
address serviceFeeReceiver_,
uint256 serviceFee_

Is this the right way for interacting with a smart contract?

I'm trying to call claimItem function on my ERC721 token. This function basically calls _safeMint() function from OpenZeppelin ERC721 contract. Here is the question. As a first step I managed deploy a contract on polygon mumbai testnet and I can see my contract on polygon scan. But when I tried to call claimItem function, inside a Transaciton Transaction(from:EthereumAddress.fromHex(session!.accounts[0]) ,to:EthereumAddress.fromHex("CONTRACT_ADDRESS"), data: bytes); like this. Transaction completes but NFT is not minted.
Here Are Some Scree Shots
This one is from a successful one (This is deployed and minted with truffle & web3)
This one is from unsuccessful one (This is signed by MetaMask)
I don't know what I'm doing wrong and I'm really new into blockchain.
EDIT Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
contract ArtItemNew is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
using Strings for uint256;
// Optional mapping for token URIs
mapping(uint256 => string) private _tokenURIs;
constructor(string memory _tokenName, string memory _tokenSymbol) ERC721(_tokenName, _tokenSymbol) public {
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI)
internal
virtual
{
require(
_exists(tokenId),
"ERC721Metadata: URI set of nonexistent token"
);
_tokenURIs[tokenId] = _tokenURI;
}
function claimItem(string memory tokenURI) public returns (uint256) {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(msg.sender, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}

i'm addming a value to the contract but i recevie this error "VM error revert"

contract Bank {
address public admin;
constructor() {
admin = msg.sender;
}
mapping (address => uint) balance;
mapping (address => bool) AccountActive;
function closeAccount() public payable{
AccountActive[msg.sender] = false;
//trasfer all the money from an account closed to the admin
payable(admin).transfer(balance[msg.sender]);
}
function viewbalance() public view returns(uint) {
return balance[msg.sender];
}}
when inserting a value before deployment, I get this error, and if I don't do it in this way the balance is 0, why? (sorry for this noob question)
This error is because you can not use an address ether balance from a smart contract. What I mean is that a smart contract cannot transfer ether from one address to another, because it would be really dangerous.
What you can do is to transfer the msg.value, which is the ether sent as the value of the transaction.
By the way, you should check for correct indentation and symbols placement. Those can lead to errors too.
The issue is here:
payable(admin).transfer(balance[msg.sender]);
You want to transfer money from the admin but the admin has no balance. So you need to send money. For this write this function:
function depositMoneyToAdmin() payable public {
// since it is payable, the money that you send would be stored in msg.value
(bool success,) = admin.call{value: msg.value}("");
// then add the owner's balance to the mapping so when u call viewBalance, you get the balance of owner
balance[admin]+=msg.value;
require(success,"Transfer failed!");
}
Avoid using transfer.
https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/

ERC-721: How to get all token ids?

I want to iterate over all token ids of a ethereum ERC-721 contract.
Some contracts have counting ids (0, 1, 2, 3, ...) which is easy, but some have random ids, e.g. https://etherscan.io/token/0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d#inventory
Sadly etherscan only shows the last 10000 token ids used, but I want to iterate over all 79490.
Is there a way to accomplish this? For me, everything is fine. Setup my own ethereum node, using some API.
You can loop through all Transfer() events emitted by the collection contract.
You're looking for transfers from address 0x0 (minted tokens). And excluding from the list transfers to address 0x0 (destroyed tokens).
One way to achieve this is by using the Web3 Contract getPastEvents() function (docs).
const myContract = new web3.eth.Contract(abiJson, contractAddress);
myContract.getPastEvents('Transfer', {
filter: {
_from: '0x0000000000000000000000000000000000000000'
},
fromBlock: 0
}).then((events) => {
for (let event of events) {
console.log(event.returnValues._tokenId);
}
});
There's no easy way to do it with an Ethereum node in a contract-agnostic way...the ERC-721 does not specify any interface methods that allow querying for all token ID, so unless the contract you're looking at uses sequential token ids, there's no way to guess all token ids from a simple node query.
Unless you want to iterate over the whole transaction history of the contract to get the ids of every minted NFT (you'd need an archive node for that, as a full node would not have the full transaction history) you should use an API from services that index all NFT activity.
You could use this API from CovalentHQ:
https://www.covalenthq.com/docs/api/#/0/Class-A/Get-NFT-Token-IDs-for-contract/lng=en
Or this one from Moralis:
https://docs.moralis.io/moralis-server/web3-sdk/token#getalltokenids
I needed the same with Ethers instead of Web3, here i the code snippet for ethers.js:
const getTransferEvents = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum)
const contract = new ethers.Contract("address", "abi", provider);
const events = await contract.queryFilter('Transfer', 0);
console.log(events);
};

How do I register Google Classroom API for push notifications?

I want to create a program that will receive notifications from my Google Classroom in question, and do something with that data. How can I register Google Classroom to react to an event?
I haven't made anything yet, and I don't know anything about Google's APIs. What should I do?
The process of registering the Google Classroom API for push notifications includes authentication, authorization, and parsing a request to Google to tell your Classroom to send these push notifications.
I highly recommend you have basic knowledge of the Google Cloud platform in the Java programming language, unlike me when I tried to do this. Trust me... it wasn't fun.
I do believe you will understand this concept enough to be able to transfer it to your language of choice, I did this in Java, using IntelliJ IDEA as my IDE.
Apart from the Google Classroom API, Google features another service to their collection called "Pub/Sub". Pub/Sub stands for Publisher/Subscriber. If you're familiar with how a queue works, think of Pub/Sub as a sort of refined queue system. You have a publisher, who posts messages on a "topic", and a subscriber to the topic who will pull messages from the topic, and choose to "acknowledge" the message or not. Acknowledging a message deletes it from the queue. For example, the publisher code sends the message "Hello, World" to a topic. That message will stay in the topic until a subscriber to that topic chooses to pull the message, read "Hello, World", and acknowledge the message, so it doesn't pop up again when pulling messages. The publisher can send as many messages as it wants. The publisher can send 10 messages, and the subscriber can choose to pull them all and iterate through them or just a few at a time.
This applies to this system because you're going to use a built-in function of the Google Classroom API that allows the API to act as a "publisher" and send update messages to a topic of your choosing. Then, you'll have a separate application checking for updates whenever you'd wish. To simplify it for now, you tell the Classroom API to "Please send update messages to this topic. I only want updates when the teacher edits the course work catalog in any way". This request will be followed by the Classroom API and the program will send messages to your topic whenever a teacher edits, or posts, or deletes, and such more.
If your classroom publisher sent 5 updates in a day, you'll have 5 pullable messages sent to your topic that any subscribing program of that topic can pull and acknowledge.
If you do not understand enough, in your opinion. Please, please do some research on Google Cloud Pub/Sub before continuing, since doing this basically revolves around this service.
Let's do this step-by-step...
Create a new project
Enable Classroom API and PubSub API
Enable Billing
Go to "IAM & admin"
Give the owner permission to "classroom-notifications#system.gserviceaccount.com"
Set up credentials for Classroom API and a "UI-based platform" using "User data"
Set up the consent screen. Just add an application name for now.
Create credentials as an "OAuth Client ID"
Choose Application Type > Other. Don't mind the client name
Download the JSON file. Rename it to "credentials_temp.json"
Create a Gradle based Java project (I'm using IntelliJ). Group id: temp. Artifact id: temp. Project name: temp
Add this to build.gradle under "dependencies"
compile 'com.google.api-client:google-api-client:1.23.0'
compile 'com.google.oauth-client:google-oauth-client-jetty:1.23.0'
compile 'com.google.apis:google-api-services-classroom:v1-rev135-1.23.0'
Set the sourceCompatibility variable in build.gradle to 11
Import those changes (There may be a little box at the bottom-right saying "import changes" as an option)
Put the credentials file in src/main/resources
In src/main/java, make a new class. Name it "TempTest.java"
Use this code I've written for you
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.classroom.Classroom;
import com.google.api.services.classroom.model.*;
import java.io.*;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import static com.google.api.services.classroom.ClassroomScopes.all;
public class TempTest {
private static final String APPLICATION_NAME = "Google Classroom API Java Quickstart";
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final String TOKENS_DIRECTORY_PATH = "tokens";
private static final String CREDENTIALS_FILE_PATH = "/credentials.json";
private static List<String> SCOPES = new ArrayList<>();
private static Classroom service;
private static String TOPIC_NAME = "projects/temp-260404/topics/temp";
private static String COURSE_ID = "47737005203";
static {
SCOPES.addAll(all());
}
public static void main(String... args) throws IOException, GeneralSecurityException {
final var HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
service = buildClassroomService(HTTP_TRANSPORT);
// registerForPushNotifications();
List<Course> courses = getAllCourses();
if (courses == null || courses.size() == 0)
System.out.println("No courses found.");
else {
System.out.println("\nCourses:");
for (var currentCourse : courses)
System.out.println(currentCourse.getName() + "(" + currentCourse.getId() + ")");
}
}
private static void registerForPushNotifications() throws IOException {
final var pubSupTopic = new CloudPubsubTopic()
.setTopicName(TOPIC_NAME);
final var courseWorkChangesInfo = new CourseRosterChangesInfo()
.setCourseId(COURSE_ID);
final var feed = new Feed()
.setFeedType("COURSE_WORK_CHANGES")
.set("courseWorkChangesInfo", courseWorkChangesInfo);
Registration notificationsRegistration = new Registration()
.setFeed(feed)
.setCloudPubsubTopic(pubSupTopic);
pubSupTopic.setFactory(JSON_FACTORY);
courseWorkChangesInfo.setFactory(JSON_FACTORY);
feed.setFactory(JSON_FACTORY);
notificationsRegistration.setFactory(JSON_FACTORY);
service.registrations().create(notificationsRegistration).execute();
System.out.println("Successfully registered");
}
private static Classroom buildClassroomService(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
final var serviceCredentials = getCredentials(HTTP_TRANSPORT);
return new Classroom.Builder(HTTP_TRANSPORT, JSON_FACTORY, serviceCredentials)
.setApplicationName(APPLICATION_NAME)
.build();
}
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
final var clientSecrets = loadJSONClientSecrets();
final var dataStoreFactory = new FileDataStoreFactory(new File(TOKENS_DIRECTORY_PATH));
final var authenticationFlow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(dataStoreFactory)
.setAccessType("offline")
.build();
return redirectToAuthentication(authenticationFlow);
}
private static GoogleClientSecrets loadJSONClientSecrets() throws IOException {
final var credentialFileStream = getCredentialsJSONFile();
if (credentialFileStream == null)
throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
return GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(credentialFileStream));
}
private static InputStream getCredentialsJSONFile() {
return TempTest.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
}
private static Credential redirectToAuthentication(GoogleAuthorizationCodeFlow flow) throws IOException {
final var receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
private static List<Course> getAllCourses() throws IOException {
ListCoursesResponse response = service.courses().list()
.execute();
return response.getCourses();
}
}
Go to Google Classroom and create your own Classroom for testing purposes.
Go to Pub/Sub and create a new topic. Make sure it's set to "Google managed key". Get its name under "Topic name" when it's made. There is a little button for copying the full path.
Set the TOPIC_NAME field of the class to a String containing the topic name you just copied
Run the code and authorize with all scopes. You will be redirected. Make sure you choose the same account you use Cloud Platform on.
Running it will give you a list of your courses and their ID numbers in parentheses. Copy the ID number of the test course you made. It should be outputted to the console after running your code.
Set the COURSE_ID field of the class to a String containing the ID you just copied
Uncomment line 40 and run the code again
You're done with my example
What you just did was authenticate yourself so Google Knows what permissions you're giving it, and it can verify your identity. Then, you sent a JSON request to Google with information about what topic you want updates to be published to, the type of updates to get, and the specific classroom to get these updates from.
I highly, HIGHLY recommend you learn how the structure of the JSON response works here.
Perhaps start here.
This page on the Google documentation has decent information. It also shows the JSON formatting of the message you'll pull from another program using the Google Pub/Sub API. I have not included that here.
Thank you, and good luck. Sorry, I may edit this question a few times. I'm really tired right now.