Retreive a struct/list of structs as views from a smart contract - elrond

I am trying to get the data of a single struct and the data of a list of this struct in view methods in a smart contract.
The struct would be something like:
#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi, Clone)]
pub struct Stream<M: ManagedTypeApi> {
pub id: u64,
pub payment: BigUint<M>,
pub enddate: u64,
pub receiver: ManagedAddress<M>,
}
A single view would be like:
#[view(getStream)]
fn get_stream(&self, id: u64) -> Stream<Self::Api> {
let payment = self.payment( id.clone() ).get().clone();
let enddate = self.enddate( id.clone() ).get().clone();
let receiver = self.receiver( id.clone() ).get().clone();
Stream {
id,
payment,
enddate,
receiver,
}
}
in the mandos tests I would expect something like:
"expect": {
"out": [
"u64:1",
"100,000,000,000",
"u64:200,000",
"address:my_address"
]
],
but in the test I always get an un-encoded byte result like:
Want: ["u64:1", "100,000,000,000", "u64:200,000", "address:my_address"]. Have: [0x000000000000000100000005174876e8000000000000030d406d795f616464726573735f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f]
I also tried different return types such as ManagedMultiResultVec, ManagedMultiResultVec or MultiResult with ManagedVec in general. But all seem to produce this output for me.
I also could not find out how I can retrieve and decode such a result in a dApp in TypeScript with the erdjs lib.
Can someone tell me what I have missed?

In mandos, you should expect this as out:
["u64:1|biguint:100,000,000,000|u64:200,000|address:my_address"]
Or
{
"0id": "u64:1",
"1payment": "biguint:100,000,000,000",
"2enddate": "u64:200,000",
"3receiver": "address:my_address"
}
I think that should be right.
And in a Dapp, you need the ABI file of the contract and need to do something like:
const result = ...; // do transaction here
const abi = await SmartContractAbi.fromAbiPath('...abi.json');
result.setEndpointDefinition(abi.getEndpoint('get_stream'));
console.log(result.unpackOutput());
From there you can figure out how to convert the result.

Related

How can I pass enum as parameter using solana/web3.js?

I defined UserAction enum in the Solana program.
#[derive(AnchorDeserialize)]
#[derive(AnchorSerialize)]
pub enum UserAction {
VIEW,
LIKE,
SHARE,
COMMENT,
DOWNLOAD,
}
Using this in an entry point.
pub fn my_fun(ctx: Context<DoPost>, action: UserAction) -> ProgramResult {
// Do something
Ok(())
}
How can I pass enum using #solana/web3.js?
As you've noticed, there's no native way to do this in JS, so you'll have to do the encoding by hand, first a byte to define the instruction type, then all the other bytes to define the instruction data.
Here's a simple example to call VIEW, assuming that it also takes a u64:
import * as BufferLayout from '#solana/buffer-layout';
const instructionLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
BufferLayout.ns64('number'),
]);
const data = Buffer.alloc(instructionLayout.span);
instructionLayout.encode({instruction: 0, number: 42}, data);
const instruction = TransactionInstruction({
keys: [
{pubkey: myPubkey, isSigner: false, isWritable: true},
// .. add all your account keys here
],
programId: myProgramId,
data,
});
For a more complete example, check out how createAccount is defined for the system program in web3.js: https://github.com/solana-labs/solana/blob/f0a235d16fd21da11176c21297176234121a3d8c/web3.js/src/system-program.ts#L655

How to write mutation in graphql-playgraound for the output

I want to see output in graphql-playground of mutations which I don't know how to do. Code is below.
type Query {
""" Gets single referral program"""
program:Program
setting(level_id:Int!): Setting
}
type Mutation {
"""Define loyalty program for a club"""
define_program(input:DefineProgram): Program
"""Club level loyalty program configuration"""
config_program(input:ProgramConfig): Setting
get_individual_code(input:Individual): String
get_group_code(input:Group): String
}
type Program {
id: String
name: String
referral_level_id:Int
enabled:Boolean
}
input DefineProgram {
name: String!
type:ReferralType!
referral_level_id:Int!
enabled:Boolean = true
userid: Int
}
input ProgramConfig {
program_id: Int!
level_id:Int!
type:CodeType!
expirydays:String!
validity:Int!
limit:Int!
userid:Int
}
input Individual {
member_id:Int!
days_to_expire:Int!
first_name:String!
last_name:String!
email:String!
userid:Int
}
input Group {
member_id: Int!
days_to_expire:Int!
people:Int!
userid:Int
}
input Filter {
program_id: Int!
level_id: Int!
}
type Setting {
program_id: Int
program_name:String
level_id:Int
tier:String
type:CodeType
expirydays:String
validity:Int
limit:Int
}
enum CodeType {
NUMERIC
ALPHA_NUMERIC
}
enum ReferralType {
INDIVIDUAL,
GROUP,
BOTH
}
Can anyone tell me how to write for all mutations to get data in graphql-playground.
Help if anyone can.
GraphQL playground will actually give you autocomplete capabilities. It sounds like you're just starting out with consuming GraphQL, though, so I would recommend you start with the learning queries from graphql.org, and then once you're comfortable there, you should be able to put together queries / mutations for your specific examples.

How to read a hash value and get the corresponding AccountId from Substrate using RPC call?

I have a substrate node up and running with storage item as: value(Hash): Option<AccountId>. My aim is to provide the hash value (say, 0x0000000000000000000000000000000000000000000000000000000000000001 and get the corresponding account id in return).
When I do this through the UI, I get the following:
I want to perform the same task via RPC calls. After going through this blog post, I realized that my case would be to read the StorageMaps and so I began with running a few queries. If I am not wrong, the module is Substratekitties and the storage item is value. The mapping would be value to AccountId.
I ran the first two calls:
util_crypto.xxhashAsHex("Substratekitties", 128)
"0xe4a154b5ba85d6072b187ee66e4fef05"
util_crypto.xxhashAsHex("Value", 128)
"0x6b2f21989c43cc4e06ac1ad3e2027000"
But I am confused in the third call encoding: encoding the file sha256 hash. How to do that?
Running util_crypto.blake2AsHex("0000000000000000000000000000000000000000000000000000000000000001", 256)
"0x16821de47d8b3b0fa4ca43e5db1028d75207cbd1c379a4738144972b105355aa"
won't work and is not working either.
By not working I mean, I get the "null" values when executing this query.
This is the storage struct:
use frame_support::{decl_module, decl_storage, dispatch::result::Result, ensure, StorageMap};
use frame_system::ensure_signed;
use sp_runtime::DispatchError;
// pub trait Trait: balances::Trait {}
pub trait Trait: pallet_balances::Trait {}
decl_storage! {
trait Store for Module<T: Trait> as KittyStorage {
// Value: map T::Hash => Option<T::AccountId>;
// TODO: check whether this is the appropriate datatype(hash).
Value: map hasher(blake2_256) T::Hash => Option<T::AccountId>;
// Balances: map hasher(blake2_256) (T::AssetId, T::AccountId) => T::Balance;
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn set_value(origin, value: T::Hash) -> Result<(), DispatchError> {
let sender = ensure_signed(origin)?;
ensure!(!<Value<T>>::contains_key(value), "key already exists");
<Value<T>>::insert(value, sender);
Ok(())
}
}
}
Update:
My literal query:
curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "state_getStorage", "params": ["0x3fd011a1ea758d2e1b46ed3cec43fc86b2f21989c43cc4e06ac1ad3e2027000d3585436436a2253c5163fa0cfe54a648fa533ef32ea10dbd966ac438af77b71"]}' http://localhost:9933/
Hex queries:
util_crypto.xxhashAsHex("KittyStorage", 128)
"0xe3fd011a1ea758d2e1b46ed3cec43fc8"
util_crypto.xxhashAsHex("Value", 128)
"0x6b2f21989c43cc4e06ac1ad3e2027000"
util_crypto.blake2AsHex("0x0000000000000000000000000000000000000000000000000000000000001234")
"0xd3585436436a2253c5163fa0cfe54a648fa533ef32ea10dbd966ac438af77b71"
I think the issue here is you are taking the hash of a string "0000...0001" not the bytes. Try adding 0x to the front of the string.
EDIT: Try like this:
util_crypto.blake2AsHex("0x0000000000000000000000000000000000000000000000000000000000000001")
> "0x33e423980c9b37d048bd5fadbd4a2aeb95146922045405accc2f468d0ef96988"
EDIT 2: The other issue here is that you are using the wrong key.
Your example shows you are taking the storage key "Substratekitties" but your storage key is KittyStorage as specified in the decl_storage! macro.
So your first hash should be: 0xe3fd011a1ea758d2e1b46ed3cec43fc8

how to use enum in apollo-client?

the enum define in OrderTypesEnum.gql
enum OrderTypes {
full_buy
pink_buy
}
import OrderTypesEnum.gql file
import OrderTypes from '#/graphql/OrderTypesEnum.gql'`
but, How to get enum in code ?
I use OrderTypes.full_buy get some error:
self.$apollo.mutate({
mutation: createOrder,
variables: {
subjectId: self.subject.id,
types: OrderTypes.full_buy
}
})
Mutation createOrderMutation error: Invariant Violation: Schema type definitions not allowed in queries. Found: "EnumTypeDefinition"
the inspect of OrderTypes type enum
Prerequisites:
< SomeEnumType > is defined in GraphQL schema (server side, no client configuration needed)
Let's assume we have:
enum SomeEnumType {
OPTION1,
OPTION2,
OPTION3
}
Apollo Client should be configured appropriate way and connected with the GraphQL API.
Then on the client side:
export const OUR_MUTATION = gql`
mutation ourMutation($foo: SomeEnumType){
ourMutation(foo: $foo){
bar
}
}
`
Only by doing this, we can pass an enum as a variable in our query or mutation. For example, with useMutation hook we can now mutate as follows:
const [ourMutation] = useMutation(OUR_MUTATION, {
variables: {
foo: "OPTION2"
},
Since the type definition in gql tag equals the definition in Schema, GraphQL recognizes a variable as an enum type despite giving it as a string.
If we want to pass an enum to variables using typescript enums we can do it as follows:
enum SomeEnumType {
OPTION1 = 0,
OPTION2 = 1,
OPTION3 = 2
}
const [ourMutation] = useMutation(OUR_MUTATION, {
variables: {
foo: SomeEnumType[SomeEnumType.OPTION1]
},
UPDATE: String enums and type generation
Personally, I recommend using string enums if possible. The usage of string enums is more straightforward.
enum SomeEnumType {
OPTION1 = "OPTION1",
OPTION2 = "OPTION2",
OPTION3 = "OPTION3"
}
...
...
variables: {
foo: SomeEnumType.OPTION1
}
For next-level coding, enum types, and all other type definitions can be automatically generated to the frontend with graphql-codegen. I really recommend using this approach, since backend schema updates and additions directly can be directly reflected in your frontend code revealing bugs and helping you code faster and more reliable.
As the error message is suggesting, Schema type definitions not allowed in queries., you can't add an enum definition in an operation document (ExecutableDefinition). You can only have operations (query, mutation, or subscription), or fragments definitions. That is, this is invalid:
enum OrderTypes {
FULL_BUY
PINK_BUY
}
mutation createOrderMutation {
...
}
If you want to define a local enum on your client, you can use the typeDefs property during ApolloClient initialization:
const client = new ApolloClient({
cache,
typeDefs: gql`
enum OrderTypes {
FULL_BUY,
PINK_BUY
}
`,
});
And then you'll be able to see the OrderTypes enum on client-side introspection (i.e Apollo extension).
Pay attention to the client-side highlight: if you try to send a request with this enum for a non-client field (i.e without the #client directive) and it makes through your server, you'll get a schema error saying that the enum type does not exist, unless you define it on your backend.

What flag to fmt.Printf to follow pointers recursively?

I'm having trouble printing a struct when failing test cases. It's a pointer to a slice of pointers to structs, or *[]*X. The problem is that I need to know the contents of the X-structs inside the slice, but I cannot get it to print the whole chain. It only prints their addresses, since it's a pointer. I need it to follow the pointers.
This is however useless since the function I want to test modifies their contents, and modifying the test code to not use pointers just means I'm not testing the code with pointers (so that wouldn't work).
Also, just looping through the slice won't work, since the real function uses reflect and might handle more than one layer of pointers.
Simplified example:
package main
import "fmt"
func main() {
type X struct {
desc string
}
type test struct {
in *[]*X
want *[]*X
}
test1 := test{
in: &[]*X{
&X{desc: "first"},
&X{desc: "second"},
&X{desc: "third"},
},
}
fmt.Printf("%#v", test1)
}
example output:
main.test{in:(*[]*main.X)(0x10436180), want:(*[]*main.X)(nil)}
(code is at http://play.golang.org/p/q8Its5l_lL )
I don't think fmt.Printf has the functionality you are looking for.
You can use the https://github.com/davecgh/go-spew library.
spew.Dump(test1)
You can use the valast (valast.String()) library alternatively.
For the example the out is:
test{in: &[]*X{
{desc: "first"},
{desc: "second"},
{desc: "third"},
}}
For spew (spew.Sdump()) it looks like:
(main.test) {
in: (*[]*main.X)(0xc000098090)((len=3 cap=3) {
(*main.X)(0xc000088300)({
desc: (string) (len=5) "first"
}),
(*main.X)(0xc000088310)({
desc: (string) (len=6) "second"
}),
(*main.X)(0xc000088320)({
desc: (string) (len=5) "third"
})
}),
want: (*[]*main.X)(<nil>)
}

Resources