Solana: Parse Token Data - cryptocurrency

How does one parse the data in an SPL token account? It contains a binary blob and I'd like to get the token type and number of tokens.
An acceptable language is solana-cli, web3.js, or solana.py. I'm looking for any solution.

The RPC give a great way to parse the data by default. You can use getParsedAccountInfo in web3.js.
Let's take the token account at 9xqnnfeonbsEGSPgF5Wd7bf9RqXy4KP22bdaGmZbHGwp
import { Connection, PublicKey, ParsedAccountData, clusterApiUrl } from '#solana/web3.js';
(async () => {
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const tokenAccount = await connection.getParsedAccountInfo(new PublicKey('9xqnnfeonbsEGSPgF5Wd7bf9RqXy4KP22bdaGmZbHGwp'));
console.log((tokenAccount.value?.data as ParsedAccountData).parsed);
})();
/**
{
info: {
isNative: false,
mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
owner: 'Ccyrkw1FdRVsfnt7qptyUXqyffq3i59GSPN1EULqZN6i',
state: 'initialized',
tokenAmount: {
amount: '738576212',
decimals: 6,
uiAmount: 738.576212,
uiAmountString: '738.576212'
}
},
type: 'account'
}
**/
Here we can see the output of the tokenAccount has a mint of EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v(USDC) owned by address Ccyrkw1FdRVsfnt7qptyUXqyffq3i59GSPN1EULqZN6i with an amount of 738.576212. That's all the data we need from a token account.

I was recently looking for an answer to the same problem, and I ended up using the AccountLayout from #solana/spl-token-v2.
So something like this:
import { AccountLayout } from "#solana/spl-token-v2";
...
const tokenAccountInfo = await connection.getAccountInfo(tokenAccount);
const decodedTokenAccountInfo = AccountLayout.decode(tokenAccountInfo!.data);
console.log(decodedTokenAccountInfo);
/*
{
mint: PublicKey {
_bn: <BN: X>
},
owner: PublicKey {
_bn: <BN: X>
},
amount: 0n,
delegateOption: 0,
delegate: PublicKey {
_bn: <BN: X>
},
state: 1,
isNativeOption: 0,
isNative: 0n,
delegatedAmount: 0n,
closeAuthorityOption: 0,
closeAuthority: PublicKey {
_bn: <BN: X>
}
}
*/
I am assuming here that the token address is valid and the getAccountInfo function will return valid data.

Related

web3js getPastlog return empty array

i test web3js by geth,i build private net in local.i try to call getPastlog ,but return empty array.
const Web3 = require("web3");
// const web3 = new Web3(new Web3.providers.HttpProvider("https://rinkeby.infura.io/v3/770daaf97ee14e0aa77ac105bbcdd79f"));
const web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545"));
web3.eth.getPastLogs({
address:"0xF81639558D54a03D6620c5bdb2f1C2e5c87736d0",
topics: ['0xdd970dd9b5bfe707922155b058a407655cb18288b807e2216442bca8ad83d6b5'] //topics:[web3.utils.sha3("adduintevent(uint256,uint256)")]
})
.then(console.log);
address and topics is right ,because i get them by getPastEvents
contract.getPastEvents('Log', {
// filter: {myIndexedParam: [20,23], myOtherIndexedParam: '0x123456789...'}, // Using an array means OR: e.g. 20 or 23
fromBlock: 0,
toBlock: 'latest'
}, function (error, events) {
console.log(events[0].raw);
});
===========result ==========
{
data: '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a54686520576f726c642100000000000000000000000000000000000000000000',
topics: [
'0xdd970dd9b5bfe707922155b058a407655cb18288b807e2216442bca8ad83d6b5'
]
}
so, i want to konw why i can't get anything by pastlog.Who can help me...

Error connect to Spring-boot-Rsocket (Auth JWT) from web-client RSocketWebSocketClient

The connection to server with spring-boot client works good:
public RSocketAdapter() throws IOException {
requester = createRSocketRequesterBuilder()
.connectWebSocket(URI.create("ws://localhost:7878/"))
.block();
}
private RSocketRequester.Builder createRSocketRequesterBuilder() {
RSocketStrategies strategies = RSocketStrategies.builder()
.encoders(encoders -> encoders.add(new Jackson2CborEncoder()))
.decoders(decoders -> decoders.add(new Jackson2CborDecoder()))
.dataBufferFactory(new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT))
.build();
return RSocketRequester.builder().rsocketStrategies(strategies);
}
public Mono<HelloToken> signIn(String principal, String credential) {
return requester
.route("signin.v1")
.data(HelloUser.builder().userId(principal).password(credential).build())
.retrieveMono(HelloToken.class)
.doOnNext(token -> {
accessToken = token.getAccessToken();
})
.onErrorStop();
}
And server receives such frame:
Correct byte frame
But the same request from web-client:
authSocketReactiv = () => {
const maxRSocketRequestN = 2147483647;
const keepAlive = 60000;
const lifetime = 180000;
const dataMimeType = 'application/json';
const metadataMimeType = 'message/x.rsocket.authentication.bearer.v0';
var client = new RSocketClient({
serializers: {
data: JsonSerializer,
metadata: JsonSerializer,
},
setup: {
dataMimeType,
keepAlive,
lifetime,
metadataMimeType
},
transport: new RSocketWebSocketClient({
url: 'ws://localhost:7878'
},Encoders)
});
// Open the connection
client.connect().subscribe({
onComplete: socket => {
socket.requestStream({
data:{
'user_id': '0000',
'password': 'Zero4'
},
metadata:'signin.v1'
}).subscribe({
onComplete: () => console.log('complete'),
onError: error => {
console.log(error);
},
onNext: payload => {
console.log('Subscribe1');
},
onSubscribe: subscription => {
console.log('Subscribe');
subscription.request(2147483647);
},
});
},
onError: error => {
console.log(error);
},
onSubscribe: cancel => {
}
});
Forms the incorrect frame and fall with “metadata is malformed ERROR” :
Error byte frame from web
What encoding or buffering options should be used here? Thanks for any tips and suggestions.
You are likely going to want to work with composite metadata and set your metadataMimeType to MESSAGE_RSOCKET_COMPOSITE_METADATA.string.
The important bit is going to be the routing metadata, which is what tells the server how to route the incoming RSocket request.
I haven't dug through the server example code you linked on StackOverflow, but just by looking at your example code, you would supply the routing metadata with your requestStream as so:
Also, the example project you listed though references signin as a request/response so you actually don't want requestStream, but requestResponse.
socket
.requestResponse({
data: Buffer.from(JSON.stringify({
user_id: '0000',
password: 'Zero4'
})),
metadata: encodeCompositeMetadata([
[MESSAGE_RSOCKET_ROUTING, encodeRoute("signin.v1")],
]),
})
You will likely want to use BufferEncoders, as shown in this example. And additionally, I believe you should not use JsonSerializer for the metadata, but instead IdentitySerializer, which will pass the composite metadata buffer straight through, rather than trying to serialize to and from JSON.
You may still run into some issues, but I suspect that this will get you past the metadata is malformed ERROR error.
Hope that helps.
Grate thanks for the detailed advices. According to directions, this complined solution works for my case:
getAuthToken = () => {
const maxRSocketRequestN = 2147483647;
const keepAlive = 60000;
const lifetime = 180000;
const dataMimeType = APPLICATION_JSON.string;
const metadataMimeType = MESSAGE_RSOCKET_COMPOSITE_METADATA.string;
var client = new RSocketClient({
serializers: {
data: IdentitySerializer,
metadata: IdentitySerializer,
},
setup: {
dataMimeType,
keepAlive,
lifetime,
metadataMimeType
},
transport: new RSocketWebSocketClient({
url: 'ws://localhost:7878'
},BufferEncoders)
});
client.connect().then(
(socket) => {
socket.requestResponse({
data: Buffer.from(JSON.stringify({
user_id: '0000',
password: 'Zero4'
})),
metadata: encodeCompositeMetadata([
[MESSAGE_RSOCKET_ROUTING, encodeRoute("signin.v1")],
]),
}).subscribe({
onComplete: (data) => console.log(data),
onError: error =>
console.error(`Request-stream error:${error.message}`),
});
},
(error) => {
console.log("composite initial connection failed");
}
);

Trying to add a warning system to my mongodb, it works but says application didn't respond

const {Client, CommandInteraction, MessageEmbed} = require("discord.js");
const db = require("../../Structures/Schemas/InfractionDB");
module.exports = {
name: "warnings",
description: "Give a warning",
permission: "ADMINISTRATOR",
options: [
{
name: "target",
description: "Select a target.",
type: "USER",
required: true
},
{
name: "reason",
description: "Provide a reason.",
type: "STRING",
required: true
},
{
name: "evidence",
description: "Provide evidence.",
type: "STRING",
required: false
},
],
/**
*
* #param {CommandInteraction} interaction
* #param {Client} client
*/
execute(interaction, client) {
const{guild, member, options} = interaction
const Target = options.getMember("target");
const Reason = options.getString("reason");
const Evidence = options.getString("evidence") || "None provided";
const Response = new MessageEmbed()
.setColor("RED")
.setAuthor({name: "MOD BOT", iconURL: guild.iconURL()});
db.findOne({GuildID: guild.id, UserID: Target.id}, async (err,data)=> {
if(err) throw err;
if(!data || !data.WarnData) {
data = new db({
GuildID: guild.id,
UserID: Target.id,
WarnData: [
{
ExecuterID: member.id,
ExecuterTag: member.user.tag,
TargetID: Target.id,
TargetTag: Target.user.tag,
Reason: Reason,
Evidence: Evidence,
Date: parseInt(interaction.createdTimestamp / 1000)
}
],
})
} else {
const WarnDataObject ={
ExecuterID: member.id,
ExecuterTag: member.user.tag,
TargetID: Target.id,
TargetTag: Target.user.tag,
Reason: Reason,
Evidence: Evidence,
Date: parseInt(interaction.createdTimestamp / 1000)
}
data.WarnData.push(WarnDataObject)
}
data.save()
});
Response.setDescription(`Warning Added: ${Target.user.tag} | ||${Target.id}||\n**Reason**: ${Reason}\n**Evidence**:${Evidence}`);
guild.channels.cache.get("946217387336818699").send({embeds:[Response]});
}}
originally this was routed to a different collection in my db. I've tried to convert it so I can see everything in one place. but it's taken me hours and don't seem to be getting anywhere. Like I said, the data is being stored on the db, but the Response is failing. Any ideas how to fix this? There are no errors in terminal
The error i think is that your application is taking too long to respond.
you have only 3 seconds to respose.
for this i would suggest that you execute you interaction as a async function
and you should use await before trying to find it in the database. because it can take some time to find the data in the database.
The data is store in database because it has no concern with the response time but the discord api wants a reply in 3 seconds or it will fail.
I have read your code. it seems okay.
i would suggest you to use the easier way instead of using the object or json form to create the commands.
SlashCommandBuilder from #discordjs/builders
its easy to use and simple.
here is an example of how easy it is if you use SlashCommandBuilder
const { SlashCommandBuilder } = require('#discordjs/builders');
const data = new SlashCommandBuilder()
.setName('gif')
.setDescription('Sends a random gif!')
.addStringOption(option =>
option.setName('category')
.setDescription('The gif category')
.setRequired(true)
.addChoice('Funny', 'gif_funny')
.addChoice('Meme', 'gif_meme')
.addChoice('Movie', 'gif_movie'));
you can install all the dependencies using
npm install discord.js #discordjs/rest discord-api-types

discord.js api request command

const config = require(`${process.cwd()}/botconfig/config.json`)
var ee = require(`${process.cwd()}/botconfig/embed.json`)
const fetch = require("node-fetch");
const { MessageEmbed } = require(`discord.js`);
module.exports = {
//definition
name: "glifestats", //the name of the command
category: "⌨️ Programming", //the category this will be listed at, for the help cmd
aliases: [""], //every parameter can be an alias
cooldown: 4, //this will set it to a 4 second cooldown
usage: "glifestats <id>", //this is for the help command for EACH cmd
description: "check stats", //the description of the command
};
run: async (client, message, args, cmduser, text, prefix) => {
await interaction.deferReply();
const term = interaction.options.getString('term');
const query = new URLSearchParams({ term });
const { list } = await fetch(`https://api.gtaliferp.fr:8443/v1/extinction/profiles/main/${query}`)
.then(response => response.json());
}
When i try to do this command, it does nothing.
It needs to get the query from the user message and post it to the url, example:
.command 4443
bot returns the https://api.gtaliferp.fr:8443/v1/extinction/profiles/main/4443 data and postes it.
And also, i wanted to make the different data on an embed like this: data 1: data data 2: data .... but i cant do it, ( check the url provided for the data titles if you want to help with that)
So you seperated your module from the rest of the command with this line marked below
module.exports = {
//definition
name: "glifestats", //the name of the command
category: "⌨️ Programming", //the category this will be listed at, for the help cmd
aliases: [""], //every parameter can be an alias
cooldown: 4, //this will set it to a 4 second cooldown
usage: "glifestats <id>", //this is for the help command for EACH cmd
description: "check stats", //the description of the command
} //here
Also term is reqesting an option that is not defined
const term = interaction.options.getString('term');
Interaction is not defined
run: async (client, message, args, cmduser, text, prefix) => {
await interaction.deferReply();
const term = interaction.options.getString('term');
Try this
const config = require(`${process.cwd()}/botconfig/config.json`) // is this needed if not, delete
var ee = require(`${process.cwd()}/botconfig/embed.json`) // is this needed if not, delete
const fetch = require("node-fetch");
const {
MessageEmbed
} = require(`discord.js`); // is this needed if not, delete
module.exports = {
name: "glifestats", //the name of the command
category: "⌨️ Programming", //the category this will be listed at, for the help cmd
aliases: [""], //every parameter can be an alias
cooldown: 4, //this will set it to a 4 second cooldown
usage: "glifestats <id>", //this is for the help command for EACH cmd
description: "check stats", //the description of the command
options: [{
name: "term",
description: "Code to search",
required: true,
type: "STRING",
}],
run: async (client, interaction) => {
await interaction.deferReply();
const term = interaction.options.getString('term');
const url = `https://api.gtaliferp.fr:8443/v1/extinction/profiles/main/${term}`
const list = await fetch(url).then(response => response.json())
// Do something with list
console.log(list)
return interaction.followUp({
content: `List has been logged but can't be sent yet because it is an object and need to be further worked to get specific elements from it`
})
// Above seemed cleaner to me
/*
const query = new URLSearchParams({
term
});
const {
list
} = await fetch(`https://api.gtaliferp.fr:8443/v1/extinction/profiles/main/${query}`)
.then(response => response.json());
*/
}
}

Does uuidv1() generate differently?

I am confused about uuidv1(). In the following code it uses uuidv1() as a salt and encrypt a password. But I thought that uuidv1() generates different strings so that I am not able to use it to encrypting a password.
Does uuidv1() generate always the same strings?
const mongoose = require("mongoose");
const uuidv1 = require("uuid/v1");
const crypto = require("crypto");
const { ObjectId } = mongoose.Schema;
const userSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: true
},
email: {
type: String,
trim: true,
required: true
},
hashed_password: {
type: String,
required: true
},
salt: String,
...
});
// virtual field
userSchema
.virtual("password")
.set(function(password) {
// create temporary variable called _password
this._password = password;
// generate a timestamp
this.salt = uuidv1();
// encryptPassword()
this.hashed_password = this.encryptPassword(password);
})
.get(function() {
return this._password;
});
// methods
userSchema.methods = {
authenticate: function(plainText) {
return this.encryptPassword(plainText) === this.hashed_password;
},
encryptPassword: function(password) {
if (!password) return "";
try {
return crypto
.createHmac("sha1", this.salt)
.update(password)
.digest("hex");
} catch (err) {
return "";
}
}
};
uuidv1 does generate a unique output everytime which is why you save that as salt in the user model
so uuid creates salt which is like alphabet for crypting strings, uuid has like v1, v2 ... check out their npm doc, i checked it and it was simple, then your userSchema encryptPassword crypts your password using crypto (you imported it in user model) based on that "salt" alphabet and you store the outcome as hashed_password which in future will be used in comparison, based on the saved salt every time

Resources