Assume there's a contract written in near-sdk-rs, deployed, has state defined as:
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct NFT {
pub tokens: UnorderedMap<TokenId, Token>,
}
#[derive(BorshDeserialize, BorshSerialize)]
pub struct Token {
pub owner: AccountId
}
Now there're some usage of this contract, as a result some records of tokens stored on chain.
Then I'd like to update this contract by adding a field to Token:
pub struct Token {
pub owner: AccountId
pub name: String // For existing ones, this will be set to ""
}
How to do this with existing state kept (similar of doing a database migration)?
You can also see how some of the examples we've created use versioning.
See BerryClub:
#[derive(BorshDeserialize, BorshSerialize)]
pub struct AccountVersionAvocado {
pub account_id: AccountId,
pub account_index: AccountIndex,
pub balance: u128,
pub num_pixels: u32,
pub claim_timestamp: u64,
}
impl From<AccountVersionAvocado> for Account {
fn from(account: AccountVersionAvocado) -> Self {
Self {
account_id: account.account_id,
account_index: account.account_index,
balances: vec![account.balance, 0],
num_pixels: account.num_pixels,
claim_timestamp: account.claim_timestamp,
farming_preference: Berry::Avocado,
}
}
}
https://github.com/evgenykuzyakov/berryclub/blob/ad2b37045b14fa72181ab92831fb741a7c40234b/contract-rs/pixel-board/src/account.rs#L19-L39
There are others but will have to come back to this once I find them
So far I can only suggest to deploy a temporary contract with old Token struct kept as is, and NewToken implement NewToken::from_token, and a change method:
impl Token {
pub fn migrate(&mut self) {
near_sdk::env::storage_write(
"STATE",
NewToken::from_token(self).try_into_vec().unwrap()
);
}
}
After you migrate the state, you can deploy a contract with the NewToken instead of Token.
Related
I'm a newbie on Substrate and blockchain development. Currently, I'm writing a smart contract using Substrate with ink!. I've defined a custom type (struct Person in the code) and created the vector of this type in the contract's storage.
I then create contract's functions for working with that vector data. But the code fails to compile. There are errors with get_person_list() function. The first one is 'the trait bound ink_storage::Vec<Person>: WrapperTypeEncode is not satisfied'.
However, if I remove this function, the code can be compiled successfully. How can I solve this compile error? thanks.
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
mod test {
use ink_prelude::string::String;
use ink_storage::collections::Vec;
use ink_storage::traits::{
SpreadLayout,
PackedLayout,
};
use scale::{Encode, Decode};
#[derive(Debug, Clone, Encode, Decode, SpreadLayout, PackedLayout)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct Person {
name: String,
age: u32,
}
#[ink(storage)]
pub struct Test {
person_list: Vec<Person>,
}
impl Test {
#[ink(constructor)]
pub fn new() -> Self {
Self { person_list: Vec::new() }
}
#[ink(message)]
pub fn add_person(&mut self, name: String, age: u32) {
self.person_list.push(Person { name, age });
}
#[ink(message)]
pub fn get_person(&self, index: u32) -> Option<Person> {
self.person_list.get(index).cloned()
}
#[ink(message)]
pub fn person_list_count(&self) -> u32 {
self.person_list.len() as u32
}
#[ink(message)]
pub fn get_person_list(&self) -> Vec<Person> {
self.person_list.clone()
}
}
}
I think it will work if you use use ink_prelude::vec::Vec; instead of use ink_storage::collections::Vec;.
I get the below error whenever try to initialize PDA account:
Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Cross-program invocation with unauthorized signer or writable account
#[program]
pub mod myprogram {
use super::*;
pub fn initialize(ctx: Context<Initialize>, bump:u8) -> ProgramResult {
let base_account: &mut Account<BaseAccount> = &mut ctx.accounts.base_account;
base_account.bump = bump;
base_account.counter = Some(0);
return Ok(());
}
}
#[derive(Accounts)]
#[instruction(bump:u8)]
pub struct Initialize<'info> {
#[account(
seeds = [b"seed".as_ref()],
bump, init, payer = creator, space = 20000)]
pub base_account: Account<'info, BaseAccount>,
#[account(mut)]
pub creator: Signer<'info>,
#[account(address = system_program::ID)]
pub system_program: AccountInfo<'info>,
}
#[account]
#[derive(Default)]
pub struct BaseAccount {
pub counter: Option<u64>,
pub bump: u8,
}
My test code looks like this:
const [baseAccountPDA, baseAccountPDABump] = await anchor.web3.PublicKey.findProgramAddress(
[Buffer.from("seed")],
program.programId
);
await program.rpc.initialize(baseAccountPDABump, {
accounts: {
baseAccount: baseAccountPDA,
creator: program.provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
},
signers: [],
});
I have tried using a newly generated keypair as the creator, and adding that keypair to the signers, but i cannot seem to get this to work.
Turns out this code is the correct way to create a PDA :) I had a test-validator running so it was trying to execute against a previously existing contract!
as you mentioned you should close test-validator terminal before execute
anchor test
anchor run test-validator for you by itself and after test you can see the test-ledger folder inside your solana anchor project root
another point is you should add test-ledger folder to your .gitignore file.
maybe it helps.
I want to save "String" Value on Substrate
At First, I use "Vec" But It's not recognized by Polkadot JS
I use "Bytes",So I get the below error
How can I solve this problem?
Please help me.
Is it correct to use "Bytes" as a way to store strings?
If correct, how can I fix the error below?
If not, what is correct to use?
Please let me know if you have sample code
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[derive(Clone, Eq, PartialEq, Default, Encode, Decode, Hash)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
pub struct TestData<BlockNumber,Bytes> {
pub name: Bytes,
pub address: Bytes,
}
pub type TestDataOf<T> = TestData<primitives::Bytes>;
--snip--
// This pallet's storage items.
decl_storage! {
// It is important to update your storage name so that your pallet's
// storage items are isolated from other pallets.
// ---------------------------------vvvvvvvvvvvvvv
trait Store for Module<T: Trait> as TemplateModule {
pub TestDatas: map hasher(blake2_128_concat) T::AccountId => Option<TestDataOf<T>>;
}
}
--snip--
decl_module! {
/// The module declaration.
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
// Initializing errors
// this includes information about your errors in the node's metadata.
// it is needed only if you are using errors in your pallet
type Error = Error<T>;
// Initializing events
// this is needed only if you are using events in your pallet
fn deposit_event() = default;
/// regist public data
#[weight = 10_000]
pub fn register_test_data(origin, name:Bytes, address:Bytes) -> dispatch::DispatchResult {
let registerer = ensure_signed(origin)?;
let test_data = TestDataOf::<T> {
name,
address,
};
<TestDatas<T>>::insert(®isterer, test_data);
Ok(())
}
}
}
--snip--
Error is...
the trait `_::_parity_scale_codec::Encode` is not implemented for `TestData<substrate_primitives::Bytes>`
the trait `_::_parity_scale_codec::Decode` is not implemented for `TestData<substrate_primitives::Bytes>`
the trait `_::_parity_scale_codec::WrapperTypeEncode` is not implemented for `substrate_primitives::Bytes`
You should use a Vec<u8> to store arbitrary bytes in runtime storage like a string.
When accessing a Vec<u8> that is a string in Polkadot JS, you should use the type Text, which automatically handles parsing and converting this type to regular UTF-8 text.
Example:
Rust:
pub struct TestData {
pub name: Vec<u8>,
pub address: Vec<u8>,
}
Polkadot JS Type Definition:
TestData: {
name: 'Text',
address: 'Text'
}
Let me know if this helps or if you have further issues.
Suppose I have a struct
pub struct Test {
pub participant: Set<AccountId>
}
And I created a new account and inserted to participant using the insert function, I can get len of participant using len() method.
How can I get the address?
pub fn get_address(&self) -> Vec<AccountId> {
self.participient.contains() // can't get data
}
You can do
self.participant.to_vec()
I have the following struct with impl:
#[near_bindgen]
#[derive(Default, Serialize, Deserialize, BorshDeserialize, BorshSerialize, Debug)]
pub struct MyStruct {
owner: String
}
#[near_bindgen(init => new)]
impl MyStruct {
fn new() -> Self {
Self {
owner: "bob".to_string()
}
}
fn get_owner(&self) -> String {
return self.owner;
}
}
Then I deploy the contract using near deploy my_contract --masterAccount myAccount
If I call get_owner using near-shell: near call my_contract get_owner --accountId=myAccount It always returns "" instead of the expected "bob".
It seems like the new method might not get called on deployment.
Initializer doesn't automatically get called on deploy. deploy just deploys the code and doesn't call anything on the contract. We should probably add a new method to shell, that does deploy_and_call. But for now just call new manually.
The reason why we don't initialize automatically is that initializer might take additional arguments. You can pass an owner to new method. Here is an example how to use initializer with custom arguments and as well as how to make sure a contract can't be called without initialization:
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct FunToken {
/// AccountID -> Account details.
pub accounts: Map<AccountId, Account>,
/// Total supply of the all token.
pub total_supply: Balance,
}
impl Default for FunToken {
fn default() -> Self {
env::panic(b"Not initialized");
unreachable!();
}
}
#[near_bindgen(init => new)]
impl FunToken {
pub fn new(owner_id: AccountId, total_supply: Balance) -> Self {
let mut ft = Self { accounts: Map::new(b"a".to_vec()), total_supply };
let mut account = ft.get_account(&owner_id);
account.balance = total_supply;
ft.accounts.insert(&owner_id, &account);
ft
}
}
From here: https://github.com/nearprotocol/near-bindgen/blob/master/examples/fun-token/src/lib.rs#L52-L77
Basically it panics during Default call, so non initialized contract can't be called.
Initializer functions are usually used when you need to parametrize the initialization of the contract. If there are no parameters then just implement Default trait:
impl Default for MyStruct {
fn default() -> Self {
Self {
owner: "bob".to_string()
}
}}