diff --git a/Cargo.lock b/Cargo.lock index 07652f759..1a95e9180 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "Inflector" version = "0.11.4" @@ -5300,7 +5302,9 @@ dependencies = [ "chainbridge", "frame-support", "frame-system", + "pallet-assets", "pallet-balances", + "pallet-timestamp", "parity-scale-codec", "serde", "sp-core", @@ -5334,8 +5338,10 @@ dependencies = [ "frame-support", "frame-system", "pallet-assets", + "pallet-balances", "pallet-timestamp", "parity-scale-codec", + "serde", "sp-application-crypto", "sp-core", "sp-io", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f39e2992b..917026f8a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -59,7 +59,7 @@ use sp_runtime::curve::PiecewiseLinear; use sp_runtime::transaction_validity::{TransactionValidity, TransactionSource, TransactionPriority}; use sp_runtime::traits::{ self, BlakeTwo256, Block as BlockT, StaticLookup, SaturatedConversion, - ConvertInto, OpaqueKeys, NumberFor, Saturating, + ConvertInto, OpaqueKeys, NumberFor, }; use sp_version::RuntimeVersion; #[cfg(any(feature = "std", test))] diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 3e4a2c00a..64755dc50 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -954,7 +954,8 @@ pub mod pallet { #[pallet::genesis_config] pub struct GenesisConfig { - pub assets: Vec<(T::AssetId, T::AccountId, T::AccountId, u32, T::Balance)> + pub assets: Vec<(T::AssetId, T::AccountId, T::AccountId, u32, T::Balance)>, + pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)> } #[cfg(feature = "std")] @@ -962,6 +963,7 @@ pub mod pallet { fn default() -> Self { Self { assets: Default::default(), + accounts: Default::default(), } } } @@ -985,6 +987,15 @@ pub mod pallet { is_frozen: false, }); } + for account in &self.accounts { + let (id, owner, balance) = account; + Account::::insert(id, owner, AssetBalance { + balance:balance.clone(), + is_frozen: false, + is_zombie: false + }); + } + } } } @@ -1177,6 +1188,7 @@ impl Pallet { id, &who, |maybe_account| -> Result { + let mut account = maybe_account.take().ok_or(Error::::BalanceZero)?; let mut burned = amount.min(account.balance); account.balance -= burned; diff --git a/frame/social-bridge/src/lib.rs b/frame/social-bridge/src/lib.rs index e8446cc82..991a81c1e 100644 --- a/frame/social-bridge/src/lib.rs +++ b/frame/social-bridge/src/lib.rs @@ -7,7 +7,6 @@ use frame_support::{ decl_error, decl_event, decl_module, dispatch::DispatchResult, ensure, }; use frame_system::{self as system, ensure_signed}; -use pallet_social_nft as erc721; use sp_arithmetic::traits::SaturatedConversion; use sp_core::U256; use sp_std::prelude::*; @@ -21,7 +20,7 @@ type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; pub trait Config: - system::Config + bridge::Config + pallet_assets::Config + erc721::Config + system::Config + bridge::Config + pallet_assets::Config + pallet_social_nft::Config { type Event: From> + Into<::Event>; /// Specifies the origin check provided by the bridge for calls that can only be called by the bridge pallet @@ -100,14 +99,14 @@ decl_module! { >::transfer_fungible(dest_id, resource_id, recipient, U256::from(value.saturated_into::())) } - /// Transfer a non-fungible token (erc721) to a (whitelisted) destination chain. + /// Transfer a non-fungible token (pallet_social_nft) to a (whitelisted) destination chain. #[weight = 195_000_000] pub fn transfer_erc721(origin, recipient: Vec, token_id: U256, dest_id: bridge::ChainId) -> DispatchResult { let source = ensure_signed(origin)?; ensure!(>::chain_whitelisted(dest_id), Error::::InvalidTransfer); - match >::tokens(&token_id) { + match >::tokens(&token_id) { Some(token) => { - >::burn_token(source, token_id)?; + >::burn_token(source, token_id)?; let resource_id = T::Erc721Id::get(); let tid: &mut [u8] = &mut[0; 32]; token_id.to_big_endian(tid); @@ -137,11 +136,11 @@ decl_module! { Ok(()) } - /// Allows the bridge to issue new erc721 tokens + /// Allows the bridge to issue new pallet_social_nft tokens #[weight = 195_000_000] - pub fn mint_erc721(origin, recipient: T::AccountId, id: U256, metadata: Vec, r_id: ResourceId) -> DispatchResult { + pub fn mint_erc721(origin, recipient: T::AccountId, id: U256, metadata: Vec, royalty: T::Balance, r_id: ResourceId) -> DispatchResult { T::BridgeOrigin::ensure_origin(origin)?; - >::mint_token(recipient, id, metadata)?; + >::mint_token(recipient, id, metadata, royalty)?; Ok(()) } } diff --git a/frame/social-nft/Cargo.toml b/frame/social-nft/Cargo.toml index ddb551423..ef731cba1 100644 --- a/frame/social-nft/Cargo.toml +++ b/frame/social-nft/Cargo.toml @@ -16,6 +16,8 @@ sp-std = { version = '3.0.0', default-features = false, path = '../../primitives sp-runtime = { version = '3.0.0', default-features = false, path = '../../primitives/runtime' } sp-io = { version = '3.0.0', default-features = false, path = '../../primitives/io' } sp-core = { version = '3.0.0', default-features = false, path = '../../primitives/core' } +pallet-assets = { default-features = false, version = '3.0.0', path = '../assets' } +pallet-timestamp = { version = '3.0.0',default-features = false, path = '../timestamp' } [dev-dependencies] pallet-balances = { version = '3.0.0',default-features = false, path = '../balances' } @@ -33,5 +35,7 @@ std = [ 'sp-core/std', 'frame-support/std', 'frame-system/std', - 'chainbridge/std' + 'chainbridge/std', + 'pallet-assets/std', + 'pallet-timestamp/std', ] diff --git a/frame/social-nft/src/lib.rs b/frame/social-nft/src/lib.rs index b017e5e86..222a0f1cd 100644 --- a/frame/social-nft/src/lib.rs +++ b/frame/social-nft/src/lib.rs @@ -6,25 +6,38 @@ use frame_support::{ decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure, traits::Get, }; -use frame_system::{self as system, ensure_root, ensure_signed}; +use frame_system::{self as system, ensure_signed}; use sp_core::U256; use sp_runtime::RuntimeDebug; use sp_std::prelude::*; - +use sp_runtime::traits::{Zero, One}; +use sp_runtime::{ + traits::Saturating, +}; +#[cfg(test)] mod mock; +#[cfg(test)] mod tests; type NftId = U256; #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] -pub struct Erc721Token { +pub struct Erc721Token { pub id: NftId, pub metadata: Vec, + pub royalty: T::Balance, } -pub trait Config: system::Config { - type Event: From> + Into<::Event>; +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] +pub struct BidToken { + pub amount: T::Balance, + pub token_id: T::AssetId, + pub dead_line: T::Moment, + pub is_active: bool +} +pub trait Config: system::Config + pallet_assets::Config + pallet_timestamp::Config { + type Event: From> + Into<::Event>; /// Some identifier for this token type, possibly the originating ethereum address. /// This is not explicitly used for anything, but may reflect the bridge's notion of resource ID. type Identifier: Get<[u8; 32]>; @@ -41,6 +54,10 @@ decl_event! { Transferred(AccountId, AccountId, NftId), /// Token removed from the system Burned(NftId), + /// Set Ask Amount + SetAskAmount(NftId), + /// Set Ask Amount + SetBidAmount(NftId), } } @@ -52,19 +69,28 @@ decl_error! { TokenAlreadyExists, /// Origin is not owner NotOwner, + /// Not For Sale + NotForSale, } } decl_storage! { trait Store for Module as SocialNFT { /// Maps tokenId to Erc721 object - pub Tokens get(fn tokens): map hasher(opaque_blake2_256) NftId => Option; + pub Tokens get(fn tokens): map hasher(opaque_blake2_256) NftId => Option>; /// Maps tokenId to owner - pub TokenOwner get(fn owner_of): map hasher(opaque_blake2_256) NftId => Option; + pub TokenCreatorAndOwner get(fn owner_of): map hasher(opaque_blake2_256) NftId => (T::AccountId, T::AccountId); /// Total number of tokens in existence pub TokenCount get(fn token_count): U256 = U256::zero(); /// Maximum token id pub MaxTokenId get(fn max_token_id): U256 = U256::zero(); + /// Set ask amount for token id + pub TokenAskAmount get(fn ask_token): double_map hasher(opaque_blake2_256) NftId, + hasher(twox_64_concat) T::AssetId => (T::Balance, T::AccountId); + /// Maps tokenId to owner + pub TokenBidAmount get(fn bid_token): double_map hasher(opaque_blake2_256) NftId, + hasher(twox_64_concat) T::AccountId => + Option>; } } @@ -75,10 +101,10 @@ decl_module! { /// Creates a new token with the given token ID and metadata, and gives ownership to owner #[weight = 195_000_000] - pub fn mint(origin, owner: T::AccountId, id: NftId, metadata: Vec) -> DispatchResult { + pub fn mint(origin, owner: T::AccountId, id: NftId, metadata: Vec, royalty: T::Balance) -> DispatchResult { let _sender = ensure_signed(origin)?; - Self::mint_token(owner, id, metadata)?; + Self::mint_token(owner, id, metadata, royalty)?; Ok(()) } @@ -98,10 +124,45 @@ decl_module! { pub fn burn(origin, id: NftId) -> DispatchResult { let _sender = ensure_signed(origin)?; - let owner = Self::owner_of(id).ok_or(Error::::NftIdDoesNotExist)?; + ensure!(TokenCreatorAndOwner::::contains_key(id), Error::::NftIdDoesNotExist); + let (_, owner) = Self::owner_of(id); Self::burn_token(owner, id)?; + Ok(()) + } + + #[weight = 195_000_000] + pub fn set_ask(origin, nft_id: NftId, token_id: T::AssetId, amount: T::Balance) -> DispatchResult { + let sender = ensure_signed(origin)?; + + ensure!(TokenCreatorAndOwner::::contains_key(nft_id), Error::::NftIdDoesNotExist); + let (_, owner) = Self::owner_of(nft_id); + ensure!(owner == sender, Error::::NotOwner); + Self::set_ask_token(owner, nft_id, token_id, amount)?; + + Ok(()) + } + + #[weight = 195_000_000] + pub fn set_bid(origin, nft_id: NftId, token_id: T::AssetId, amount: T::Balance, dead_line: T::Moment) -> DispatchResult { + let sender = ensure_signed(origin)?; + + ensure!(TokenCreatorAndOwner::::contains_key(nft_id), Error::::NftIdDoesNotExist); + + Self::set_bid_token(sender, nft_id, token_id, amount, dead_line)?; + + Ok(()) + } + + #[weight = 195_000_000] + pub fn remove_bid(origin, nft_id: NftId) -> DispatchResult { + let sender = ensure_signed(origin)?; + + ensure!(TokenCreatorAndOwner::::contains_key(nft_id), Error::::NftIdDoesNotExist); + + Self::remove_bid_token(sender, nft_id)?; + Ok(()) } } @@ -109,13 +170,13 @@ decl_module! { impl Module { /// Creates a new token in the system. - pub fn mint_token(owner: T::AccountId, id: NftId, metadata: Vec) -> DispatchResult { - ensure!(!Tokens::contains_key(id), Error::::TokenAlreadyExists); + pub fn mint_token(owner: T::AccountId, id: NftId, metadata: Vec, royalty: T::Balance) -> DispatchResult { + ensure!(!>::contains_key(id), Error::::TokenAlreadyExists); - let new_token = Erc721Token { id, metadata }; + let new_token = Erc721Token { id, metadata, royalty }; - ::insert(&id, new_token); - >::insert(&id, owner.clone()); + >::insert(&id, new_token); + >::insert(&id, (owner.clone(), owner.clone())); let new_total = ::get().saturating_add(U256::one()); ::put(new_total); if ::get() < id { @@ -130,10 +191,11 @@ impl Module { /// Modifies ownership of a token pub fn transfer_from(from: T::AccountId, to: T::AccountId, id: NftId) -> DispatchResult { // Check from is owner and token exists - let owner = Self::owner_of(id).ok_or(Error::::NftIdDoesNotExist)?; + ensure!(TokenCreatorAndOwner::::contains_key(id), Error::::NftIdDoesNotExist); + let (_, owner) = Self::owner_of(id); ensure!(owner == from, Error::::NotOwner); // Update owner - >::insert(&id, to.clone()); + TokenCreatorAndOwner::::mutate(id, |(_, owner)| *owner = to.clone()); Self::deposit_event(RawEvent::Transferred(from, to, id)); @@ -142,11 +204,12 @@ impl Module { /// Deletes a token from the system. pub fn burn_token(from: T::AccountId, id: NftId) -> DispatchResult { - let owner = Self::owner_of(id).ok_or(Error::::NftIdDoesNotExist)?; + ensure!(TokenCreatorAndOwner::::contains_key(id), Error::::NftIdDoesNotExist); + let (_, owner) = Self::owner_of(id); ensure!(owner == from, Error::::NotOwner); - ::remove(&id); - >::remove(&id); + >::remove(&id); + >::remove(&id); let new_total = ::get().saturating_sub(U256::one()); ::put(new_total); @@ -154,4 +217,56 @@ impl Module { Ok(()) } + + pub fn set_ask_token(owner: T::AccountId, id: NftId, token_id: T::AssetId, amount: T::Balance) -> DispatchResult { + + >::insert(&id, token_id, (amount, owner)); + Self::deposit_event(RawEvent::SetAskAmount(id)); + Ok(()) + } + + pub fn set_bid_token(sender: T::AccountId, id: NftId, token_id: T::AssetId, amount: T::Balance, dead_line: T::Moment) -> DispatchResult { + + let (ask_token, _) = TokenAskAmount::::get(id, token_id); + + ensure!(!ask_token.is_zero(), Error::::NotForSale); + let now_timestamp = >::now(); + + if amount > ask_token && dead_line >=now_timestamp { + Self::execute_trade(sender, id, token_id, amount); + } else { + let bid = BidToken{ + amount, + token_id, + dead_line, + is_active: true + }; + >::insert(&id, &sender, bid); + Self::deposit_event(RawEvent::SetBidAmount(id)); + } + + Ok(()) + } + + pub fn remove_bid_token(sender: T::AccountId, id: NftId) -> DispatchResult { + ensure!(TokenBidAmount::::contains_key(id, sender.clone()), Error::::NftIdDoesNotExist); + TokenBidAmount::::mutate(id, &sender, |bid| + if let Some(b) = bid { + b.is_active = false + } + ); + + Ok(()) + } + + fn execute_trade(sender: T::AccountId, id: NftId, token_id: T::AssetId, amount: T::Balance) -> DispatchResult { + let nft = Tokens::::get(id).ok_or(Error::::NftIdDoesNotExist)?; + let (creator, owner) = Self::owner_of(id); + if nft.royalty.is_zero() { + >::do_transfer(token_id, sender.clone(), creator, amount.saturating_mul(nft.royalty)); + >::do_transfer(token_id, sender.clone(), owner, amount.saturating_mul(T::Balance::one().saturating_sub(nft.royalty))); + TokenCreatorAndOwner::::mutate(id, |(_, owner)| *owner = sender); + } + Ok(()) + } } diff --git a/frame/social-nft/src/mock.rs b/frame/social-nft/src/mock.rs index 502974611..add216e81 100644 --- a/frame/social-nft/src/mock.rs +++ b/frame/social-nft/src/mock.rs @@ -6,16 +6,16 @@ use sp_core::hashing::blake2_128; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, Block as BlockT, IdentityLookup}, + traits::{BlakeTwo256, IdentityLookup}, BuildStorage, }; -use crate::{self as erc721, Config}; +use crate::{self as pallet_social_nft, Config}; use chainbridge as bridge; pub use pallet_balances as balances; -pub type Block = sp_runtime::generic::Block; -pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test where @@ -25,16 +25,12 @@ frame_support::construct_runtime!( { System: system::{Module, Call, Event}, Balances: balances::{Module, Call, Storage, Config, Event}, - Erc721: erc721::{Module, Call, Storage, Event}, + Assets: pallet_assets::{Module, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent}, + SocialNft: pallet_social_nft::{Module, Call, Storage, Event}, } ); -pub const USER_A: u64 = 0x1; -pub const USER_B: u64 = 0x2; -pub const USER_C: u64 = 0x3; -pub const ENDOWED_BALANCE: u64 = 100_000_000; - - parameter_types! { pub const BlockHashCount: u64 = 250; pub BlockWeights: frame_system::limits::BlockWeights = @@ -83,6 +79,40 @@ impl pallet_balances::Config for Test { type WeightInfo = (); } +type Moment = u128; + +parameter_types! { + pub const MinimumPeriod: Moment = 5; + } +impl pallet_timestamp::Config for Test { + type Moment = Moment; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +parameter_types! { + pub const AssetDepositBase: u64 = 1; + pub const AssetDepositPerZombie: u64 = 1; + pub const StringLimit: u32 = 50; + pub const MetadataDepositBase: u64 = 1; + pub const MetadataDepositPerByte: u64 = 1; + } + +impl pallet_assets::Config for Test { + type Currency = Balances; + type Event = Event; + type Balance = u128; + type AssetId = u32; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDepositBase = AssetDepositBase; + type AssetDepositPerZombie = AssetDepositPerZombie; + type StringLimit = StringLimit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type WeightInfo = (); +} + parameter_types! { pub Erc721Id: bridge::ResourceId = bridge::derive_resource_id(1, &blake2_128(b"NFT")); } @@ -92,13 +122,17 @@ impl Config for Test { type Identifier = Erc721Id; } +pub const USER_A: u64 = 0x1; +pub const USER_B: u64 = 0x2; +pub const USER_C: u64 = 0x3; +pub const ENDOWED_BALANCE: u64 = 100_000_000; +pub const ROYALTY:u128 = 2; + pub fn new_test_ext() -> sp_io::TestExternalities { - GenesisConfig { - balances: Some(balances::GenesisConfig { - balances: vec![(USER_A, ENDOWED_BALANCE)], - }), - } - .build_storage() - .unwrap() - .into() + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(USER_A, ENDOWED_BALANCE)], + }.assimilate_storage(&mut t).unwrap(); + + t.into() } diff --git a/frame/social-nft/src/tests.rs b/frame/social-nft/src/tests.rs index 23f11476f..8741d9c98 100644 --- a/frame/social-nft/src/tests.rs +++ b/frame/social-nft/src/tests.rs @@ -1,6 +1,6 @@ #![cfg(test)] -use super::mock::{new_test_ext, Erc721, Origin, Test, USER_A, USER_B, USER_C}; +use super::mock::{new_test_ext, SocialNft, Origin, Test, USER_A, USER_B, USER_C, ROYALTY}; use super::*; use frame_support::{assert_noop, assert_ok}; use sp_core::U256; @@ -13,57 +13,61 @@ fn mint_burn_tokens() { let metadata_a: Vec = vec![1, 2, 3]; let metadata_b: Vec = vec![4, 5, 6]; - assert_ok!(Erc721::mint( + assert_ok!(SocialNft::mint( Origin::signed(1), // Origin::root(), USER_A, id_a, - metadata_a.clone() + metadata_a.clone(), + ROYALTY )); assert_eq!( - Erc721::tokens(id_a).unwrap(), + SocialNft::tokens(id_a).unwrap(), Erc721Token { id: id_a, - metadata: metadata_a.clone() + metadata: metadata_a.clone(), + royalty: ROYALTY } ); - assert_eq!(Erc721::token_count(), 1.into()); - assert_eq!(Erc721::max_token_id(), 1.into()); + assert_eq!(SocialNft::token_count(), 1.into()); + assert_eq!(SocialNft::max_token_id(), 1.into()); assert_noop!( - Erc721::mint(Origin::signed(1), USER_A, id_a, metadata_a), // Erc721::mint(Origin::root(), USER_A, id_a, metadata_a), + SocialNft::mint(Origin::signed(1), USER_A, id_a, metadata_a, ROYALTY), // SocialNft::mint(Origin::root(), USER_A, id_a, metadata_a), Error::::TokenAlreadyExists ); - assert_ok!(Erc721::mint( + assert_ok!(SocialNft::mint( Origin::signed(1), // Origin::root(), USER_A, id_b, - metadata_b.clone() + metadata_b.clone(), + ROYALTY )); assert_eq!( - Erc721::tokens(id_b).unwrap(), + SocialNft::tokens(id_b).unwrap(), Erc721Token { id: id_b, - metadata: metadata_b.clone() + metadata: metadata_b.clone(), + royalty: ROYALTY } ); - assert_eq!(Erc721::token_count(), 2.into()); - assert_eq!(Erc721::max_token_id(), 2.into()); + assert_eq!(SocialNft::token_count(), 2.into()); + assert_eq!(SocialNft::max_token_id(), 2.into()); assert_noop!( - Erc721::mint(Origin::signed(1), USER_A, id_b, metadata_b), // Erc721::mint(Origin::root(), USER_A, id_b, metadata_b), + SocialNft::mint(Origin::signed(1), USER_A, id_b, metadata_b, ROYALTY), // SocialNft::mint(Origin::root(), USER_A, id_b, metadata_b), Error::::TokenAlreadyExists ); - assert_ok!(Erc721::burn(Origin::signed(1), id_a)); // assert_ok!(Erc721::burn(Origin::root(), id_a)); - assert_eq!(Erc721::token_count(), 1.into()); - assert_eq!(Erc721::max_token_id(), 2.into()); - assert!(!::contains_key(&id_a)); - assert!(!>::contains_key(&id_a)); + assert_ok!(SocialNft::burn(Origin::signed(1), id_a)); // assert_ok!(SocialNft::burn(Origin::root(), id_a)); + assert_eq!(SocialNft::token_count(), 1.into()); + assert_eq!(SocialNft::max_token_id(), 2.into()); + assert!(!>::contains_key(&id_a)); + assert!(!>::contains_key(&id_a)); - assert_ok!(Erc721::burn(Origin::signed(1), id_b)); // assert_ok!(Erc721::burn(Origin::root(), id_b)); - assert_eq!(Erc721::token_count(), 0.into()); - assert_eq!(Erc721::max_token_id(), 2.into()); - assert!(!::contains_key(&id_b)); - assert!(!>::contains_key(&id_b)); + assert_ok!(SocialNft::burn(Origin::signed(1), id_b)); // assert_ok!(SocialNft::burn(Origin::root(), id_b)); + assert_eq!(SocialNft::token_count(), 0.into()); + assert_eq!(SocialNft::max_token_id(), 2.into()); + assert!(!>::contains_key(&id_b)); + assert!(!>::contains_key(&id_b)); }) } @@ -75,19 +79,124 @@ fn transfer_tokens() { let metadata_a: Vec = vec![1, 2, 3]; let metadata_b: Vec = vec![4, 5, 6]; - assert_ok!(Erc721::mint(Origin::signed(1), USER_A, id_a, metadata_a)); // assert_ok!(Erc721::mint(Origin::root(), USER_A, id_a, metadata_a)); - assert_ok!(Erc721::mint(Origin::signed(1), USER_A, id_b, metadata_b)); // assert_ok!(Erc721::mint(Origin::root(), USER_A, id_b, metadata_b)); + assert_ok!(SocialNft::mint(Origin::signed(1), USER_A, id_a, metadata_a, ROYALTY)); // assert_ok!(SocialNft::mint(Origin::root(), USER_A, id_a, metadata_a)); + assert_ok!(SocialNft::mint(Origin::signed(1), USER_A, id_b, metadata_b, ROYALTY)); // assert_ok!(SocialNft::mint(Origin::root(), USER_A, id_b, metadata_b)); - assert_ok!(Erc721::transfer(Origin::signed(USER_A), USER_B, id_a)); - assert_eq!(Erc721::owner_of(id_a).unwrap(), USER_B); + assert_ok!(SocialNft::transfer(Origin::signed(USER_A), USER_B, id_a)); + assert_eq!(SocialNft::owner_of(id_a).1, USER_B); - assert_ok!(Erc721::transfer(Origin::signed(USER_A), USER_C, id_b)); - assert_eq!(Erc721::owner_of(id_b).unwrap(), USER_C); + assert_ok!(SocialNft::transfer(Origin::signed(USER_A), USER_C, id_b)); + assert_eq!(SocialNft::owner_of(id_b).1, USER_C); - assert_ok!(Erc721::transfer(Origin::signed(USER_B), USER_A, id_a)); - assert_eq!(Erc721::owner_of(id_a).unwrap(), USER_A); + assert_ok!(SocialNft::transfer(Origin::signed(USER_B), USER_A, id_a)); + assert_eq!(SocialNft::owner_of(id_a).1, USER_A); - assert_ok!(Erc721::transfer(Origin::signed(USER_C), USER_A, id_b)); - assert_eq!(Erc721::owner_of(id_b).unwrap(), USER_A); + assert_ok!(SocialNft::transfer(Origin::signed(USER_C), USER_A, id_b)); + assert_eq!(SocialNft::owner_of(id_b).1, USER_A); }) } + +#[test] +fn set_ask_tokens() { + new_test_ext().execute_with(|| { + let id_a: U256 = 1.into(); + let metadata_a: Vec = vec![1, 2, 3]; + assert_noop!( + SocialNft::set_ask(Origin::signed(1), id_a, 1, 1), + Error::::NftIdDoesNotExist + ); + + assert_ok!(SocialNft::mint( + Origin::signed(1), // Origin::root(), + USER_A, + id_a, + metadata_a.clone(), + ROYALTY + )); + + assert_noop!( + SocialNft::set_ask(Origin::signed(11), id_a, 1, 1), + Error::::NotOwner + ); + + assert_ok!( + SocialNft::set_ask(Origin::signed(USER_A), id_a, 1, 1) + ); + + assert!(>::contains_key(id_a, 1)); + }) +} + +#[test] +fn set_bid_tokens() { + new_test_ext().execute_with(|| { + let id_a: U256 = 1.into(); + let metadata_a: Vec = vec![1, 2, 3]; + assert_noop!( + SocialNft::set_bid(Origin::signed(1), id_a, 1, 1, 1), + Error::::NftIdDoesNotExist + ); + + assert_ok!(SocialNft::mint( + Origin::signed(1), // Origin::root(), + USER_A, + id_a, + metadata_a.clone(), + ROYALTY + )); + + assert_ok!( + SocialNft::set_ask(Origin::signed(USER_A), id_a, 1, 0) + ); + + assert_noop!( + SocialNft::set_bid(Origin::signed(USER_A), id_a, 1, 1, 1), + Error::::NotForSale + ); + + assert_ok!( + SocialNft::set_ask(Origin::signed(USER_A), id_a, 1, 1) + ); + + assert_ok!( + SocialNft::set_bid(Origin::signed(USER_A), id_a, 1, 1, 1) + ); + + assert!(>::contains_key(id_a, 1)); + }) +} + +#[test] +fn remove_bid_tokens() { + new_test_ext().execute_with(|| { + let id_a: U256 = 1.into(); + let metadata_a: Vec = vec![1, 2, 3]; + + assert_noop!( + SocialNft::remove_bid(Origin::signed(1), id_a), + Error::::NftIdDoesNotExist + ); + + assert_ok!(SocialNft::mint( + Origin::signed(1), // Origin::root(), + USER_A, + id_a, + metadata_a.clone(), + ROYALTY + )); + + assert_ok!( + SocialNft::set_ask(Origin::signed(USER_A), id_a, 1, 1) + ); + + assert_ok!( + SocialNft::set_bid(Origin::signed(USER_A), id_a, 1, 1, 1) + ); + + assert_ok!( + SocialNft::remove_bid(Origin::signed(1), id_a), + ); + + assert!(!SocialNft::bid_token(id_a, 1).unwrap().is_active); + }) +} diff --git a/frame/social-swap/src/mock.rs b/frame/social-swap/src/mock.rs index 72fc3aee1..57374f54a 100644 --- a/frame/social-swap/src/mock.rs +++ b/frame/social-swap/src/mock.rs @@ -129,6 +129,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pallet_assets::GenesisConfig:: { assets: vec![(ASSET_ID, OWNER, OWNER, MAX_ZOMBIES, MIN_BALANCE)], + accounts: vec![(ASSET_ID, OWNER, INITIAL_BALANCE)], }.assimilate_storage(&mut t).unwrap(); t.into() diff --git a/frame/social-swap2/Cargo.toml b/frame/social-swap2/Cargo.toml index 9082f9098..54af5458a 100644 --- a/frame/social-swap2/Cargo.toml +++ b/frame/social-swap2/Cargo.toml @@ -19,6 +19,10 @@ sp-core = { default-features = false, version = '3.0.0', path = '../../primitive pallet-timestamp = { default-features = false, version = '3.0.0', path = '../timestamp' } pallet-assets = { default-features = false, version = '3.0.0', path = '../assets' } +[dev-dependencies] +serde = "1.0.124" +pallet-balances = {version = '3.0.0', path = '../balances' } + [features] default = ['std'] std = [ diff --git a/frame/social-swap2/src/lib.rs b/frame/social-swap2/src/lib.rs index ddcefbc0e..bee1e299a 100644 --- a/frame/social-swap2/src/lib.rs +++ b/frame/social-swap2/src/lib.rs @@ -19,6 +19,11 @@ pub type BalanceOf = <::FungibleToken as Fungible< ::AccountId, >>::Balance; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + pub trait Config: frame_system::Config + pallet_assets::Config + pallet_timestamp::Config { @@ -98,7 +103,6 @@ decl_module! { #[weight = 10_000 + T::DbWeight::get().writes(1)] fn mint(origin, to: T::AccountId) -> Result<(), DispatchError> { - ensure_root(origin.clone())?; let sender = ensure_signed(origin)?; let social_token_id = Self::social_token_id(); let reserve0 = Self::reserve0(); @@ -112,16 +116,20 @@ decl_module! { let fee_on = Self::mint_fee(reserve0, reserve1)?; let total_supply = T::FungibleToken::total_supply(&social_token_id); let liquidity = if total_supply == 0u32.into() { - let minimum_liquidity = amount0 + let min_liquidity = T::MinimumLiquidity::get().saturated_into::().saturated_into(); + amount0 .saturating_mul(amount1) .integer_sqrt() - .saturating_sub(T::MinimumLiquidity::get().saturated_into::().saturated_into()); - T::FungibleToken::issue(&social_token_id, &Self::address0(), minimum_liquidity)?; - amount0.saturating_mul(amount1).saturating_sub(minimum_liquidity) + .saturating_sub(min_liquidity) } else { (amount0.saturating_mul(total_supply) / reserve0).min(amount1.saturating_mul(total_supply) / reserve1) }; ensure!(liquidity > 0u32.into(), Error::::InsufficientLiquidityMinted); + + if total_supply == 0u32.into() { + let min_liquidity = T::MinimumLiquidity::get().saturated_into::().saturated_into(); + T::FungibleToken::issue(&social_token_id, &Self::address0(), min_liquidity)?; + } T::FungibleToken::issue(&social_token_id, &to, liquidity)?; let _ = Self::update(balance0, balance1, reserve0, reserve1); @@ -136,7 +144,6 @@ decl_module! { #[weight = 10_000 + T::DbWeight::get().writes(1)] fn burn(origin, to: T::AccountId) -> Result<(), DispatchError> { - ensure_root(origin.clone())?; let sender = ensure_signed(origin)?; let social_token_id = Self::social_token_id(); let reserve0 = Self::reserve0(); @@ -150,16 +157,18 @@ decl_module! { let fee_on = Self::mint_fee(reserve0, reserve1)?; let total_supply = T::FungibleToken::total_supply(&social_token_id); + let amount0 = liquidity.saturating_mul(balance0) / total_supply; let amount1 = liquidity.saturating_mul(balance1) / total_supply; ensure!(amount0 > 0u32.into() && amount1 > 0u32.into(), Error::::InsufficientLiquidityBurned); - T::FungibleToken::burn(&social_token_id, &to, liquidity)?; - let _ = Self::safe_transfer(&Self::token0(), &to, amount0); - let _ = Self::safe_transfer(&Self::token1(), &to, amount1); + + T::FungibleToken::transfer(&social_token_id, &Self::token0(), &to, amount0); + T::FungibleToken::transfer(&social_token_id, &Self::token1(), &to, amount1); let balance0 = T::FungibleToken::balances(&social_token_id, &Self::token0()); let balance1 = T::FungibleToken::balances(&social_token_id, &Self::token1()); + let _ = Self::update(balance0, balance1, reserve0, reserve1); if fee_on { >::put(reserve0.saturating_mul(reserve1)); @@ -172,7 +181,6 @@ decl_module! { #[weight = 10_000 + T::DbWeight::get().writes(1)] fn swap(origin, amount0_out: BalanceOf, amount1_out: BalanceOf, to: T::AccountId, data: Vec) -> DispatchResult { - ensure_root(origin.clone())?; let sender = ensure_signed(origin)?; ensure!(amount0_out > 0u32.into() || amount1_out > 0u32.into(), Error::::InsufficientOutputAmount); let social_token_id = Self::social_token_id(); @@ -184,10 +192,10 @@ decl_module! { ensure!(to != token0 && to != token1, Error::::InvalidTo); if amount0_out > 0u32.into() { - Self::safe_transfer(&token0, &to, amount0_out)? + T::FungibleToken::transfer(&social_token_id, &token0, &to, amount0_out); } if amount1_out > 0u32.into() { - Self::safe_transfer(&token1, &to, amount1_out)? + T::FungibleToken::transfer(&social_token_id, &token1, &to, amount1_out); } // TODO: // if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data); @@ -319,7 +327,6 @@ impl Module { amount.saturated_into::().saturated_into(), ExistenceRequirement::AllowDeath, )?; - Ok(()) } } diff --git a/frame/social-swap2/src/mock.rs b/frame/social-swap2/src/mock.rs new file mode 100644 index 000000000..8eb30c8ea --- /dev/null +++ b/frame/social-swap2/src/mock.rs @@ -0,0 +1,141 @@ +use crate as pallet_social_swap2; +use sp_core::H256; +use frame_support::parameter_types; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, testing::Header, +}; +use frame_system as system; +use sp_runtime::traits::Convert; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Module, Call, Config, Storage, Event}, + Balances: pallet_balances::{Module, Call, Storage, Config, Event}, + Assets: pallet_assets::{Module, Call, Storage, Event}, + SocialSwap2: pallet_social_swap2::{Module, Call, Storage, Event}, + Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} +pub type Balance = u128; + +impl system::Config for Test { + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = Call; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; + } +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type Balance = u128; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +parameter_types! { + pub const AssetDepositBase: u64 = 1; + pub const AssetDepositPerZombie: u64 = 1; + pub const StringLimit: u32 = 50; + pub const MetadataDepositBase: u64 = 1; + pub const MetadataDepositPerByte: u64 = 1; + } + +impl pallet_assets::Config for Test { + type Currency = Balances; + type Event = Event; + type Balance = u128; + type AssetId = u32; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDepositBase = AssetDepositBase; + type AssetDepositPerZombie = AssetDepositPerZombie; + type StringLimit = StringLimit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type WeightInfo = (); +} +pub const MINIMUM_LIQUIDITY:u64 = 1000; + +parameter_types! { + pub const MinimumLiquidity: u64 = MINIMUM_LIQUIDITY; +} + +impl pallet_social_swap2::Config for Test { + type Currency = Balances; + type Event = Event; + type FungibleToken = Assets; + type MinimumLiquidity = MinimumLiquidity; +} +pub const ASSET_ID:u32 = 1; +pub const ACCOUNT1:u64 = 1; +pub const ACCOUNT2:u64 = 2; +pub const ACCOUNT3:u64 = 3; +pub const MAX_ZOMBIES:u32 = 3; +pub const MIN_BALANCE:u128 = 1; +pub const INITIAL_BALANCE:u128 = 100_000_0; +pub const TOKEN0:u64 = 10; +pub const TOKEN1:u64 = 11; +pub const FEE_TO:u64 = 12; +pub const ADDRESS0:u64 = 13; +pub const TREASURY:u64 = 14; +pub const INITIAL_SUPPLY: u128 = 1_000_000_000_000_000_000_0000; + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(ACCOUNT1, INITIAL_BALANCE), (ACCOUNT2, INITIAL_BALANCE)], + }.assimilate_storage(&mut t).unwrap(); + + pallet_assets::GenesisConfig:: { + assets: vec![(ASSET_ID, ACCOUNT1, ACCOUNT1, MAX_ZOMBIES, MIN_BALANCE)], + accounts: vec![(ASSET_ID, ACCOUNT1, INITIAL_SUPPLY), (ASSET_ID, TREASURY, 0u128)], + }.assimilate_storage(&mut t).unwrap(); + + t.into() +} diff --git a/frame/social-swap2/src/tests.rs b/frame/social-swap2/src/tests.rs new file mode 100644 index 000000000..f62482c35 --- /dev/null +++ b/frame/social-swap2/src/tests.rs @@ -0,0 +1,259 @@ +use crate::{mock::*, Error}; +use super::*; +use frame_support::{assert_noop, assert_ok}; +use codec::Encode; + +#[test] +fn test_mint_should_not_work() { + new_test_ext().execute_with(|| { + >::put(INITIAL_BALANCE); + assert_noop!( + SocialSwap2::mint( + Origin::signed(ACCOUNT1), + ACCOUNT3 + ), + Error::::NotEnoughLiquidity + ); + }); +} + +#[test] +fn test_mint_should_work() { + new_test_ext().execute_with(|| { + + SocialSwap2::initialize(Origin::root(), FEE_TO, ADDRESS0, TREASURY, TOKEN0, TOKEN1); + let token_0_amount: u128 = 1_000_000_000_000_000_000; + let token_1_amount: u128 = 4_000_000_000_000_000_000; + pallet_assets::Module::::transfer(&ASSET_ID, &ACCOUNT1, &TOKEN0, token_0_amount); + pallet_assets::Module::::transfer(&ASSET_ID, &ACCOUNT1, &TOKEN1, token_1_amount); + let expected_liquidity: u128 = 2_000_000_000_000_000_000u128; + + assert_eq!( + pallet_assets::Module::::total_supply(ASSET_ID), + 0 + ); + assert_ok!( + SocialSwap2::mint( + Origin::signed(ACCOUNT1), + ACCOUNT2 + ) + ); + + assert_eq!( + pallet_assets::Module::::total_supply(ASSET_ID), + expected_liquidity + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, ACCOUNT2), + expected_liquidity - MINIMUM_LIQUIDITY as u128 + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, TOKEN0), + token_0_amount + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, TOKEN1), + token_1_amount + ); + + assert_eq!( + SocialSwap2::reserve0(), + token_0_amount + ); + + assert_eq!( + SocialSwap2::reserve1(), + token_1_amount + ); + }); +} + +#[test] +fn test_burn_should_not_work() { + new_test_ext().execute_with(|| { + assert_noop!( + SocialSwap2::mint( + Origin::signed(ACCOUNT1), + ACCOUNT3 + ), + Error::::InsufficientLiquidityMinted + ); + }); +} + +#[test] +fn test_burn_should_work() { + new_test_ext().execute_with(|| { + let token_0_amount: u128 = 3_000_000_000_000_000_000; + let token_1_amount: u128 = 3_000_000_000_000_000_000; + add_liquidity(token_0_amount, token_1_amount); + let expected_liquidity: u128 = 3_000_000_000_000_000_000u128; + + assert_noop!( + SocialSwap2::burn( + Origin::signed(ACCOUNT1), + ACCOUNT2 + ), + Error::::InsufficientLiquidityBurned + ); + + pallet_balances::Module::::deposit_creating(&TREASURY, expected_liquidity - MINIMUM_LIQUIDITY as u128); + + assert_eq!( + pallet_assets::Module::::total_supply(ASSET_ID), + expected_liquidity + ); + + + assert_ok!( + SocialSwap2::burn( + Origin::signed(ACCOUNT1), + ACCOUNT2 + ) + ); + + assert_eq!( + SocialSwap2::reserve0(), + MINIMUM_LIQUIDITY as u128 + ); + + assert_eq!( + SocialSwap2::reserve1(), + MINIMUM_LIQUIDITY as u128 + ); + + assert_eq!( + pallet_assets::Module::::total_supply(ASSET_ID), + MINIMUM_LIQUIDITY as u128 + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, TOKEN0), + MINIMUM_LIQUIDITY as u128 + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, TOKEN1), + MINIMUM_LIQUIDITY as u128 + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, ACCOUNT2), + token_0_amount + token_1_amount - 2000u128 + ); + + }); +} + +#[test] +fn test_swap_should_not_work() { + new_test_ext().execute_with(|| { + + assert_noop!(SocialSwap2::swap( + Origin::signed(ACCOUNT1), + 0, + 0, + ACCOUNT3, + "0x".encode() + ), + Error::::InsufficientOutputAmount); + + assert_noop!(SocialSwap2::swap( + Origin::signed(ACCOUNT1), + 1, + 2, + ACCOUNT3, + "0x".encode() + ), + Error::::InsufficientLiquidity); + + >::put(2); + >::put(2); + + assert_noop!(SocialSwap2::swap( + Origin::signed(ACCOUNT1), + 3, + 3, + ACCOUNT3, + "0x".encode() + ), + Error::::InsufficientLiquidity); + + SocialSwap2::initialize(Origin::root(), FEE_TO, ADDRESS0, TREASURY, TOKEN0, TOKEN0); + + assert_noop!(SocialSwap2::swap( + Origin::signed(ACCOUNT1), + 1, + 1, + TOKEN0, + "0x".encode() + ), + Error::::InvalidTo); + + }); +} + +#[test] +fn test_swap_should_work() { + new_test_ext().execute_with(|| { + let token_0_amount: u128 = 5_000_000_000_000_000_000; + let token_1_amount: u128 = 10_000_000_000_000_000_000; + add_liquidity(token_0_amount, token_1_amount); + let expected_output_amount: u128 = 1662497915624478906u128; + let swap_amount: u128 = 1_000_000_000_000_000_000; + + pallet_assets::Module::::transfer(&ASSET_ID, &ACCOUNT1, &TOKEN0, swap_amount); + + assert_ok!(SocialSwap2::swap( + Origin::signed(ACCOUNT1), + 0, + expected_output_amount, + ACCOUNT2, + "0x".encode() + ) + ); + + assert_eq!( + SocialSwap2::reserve0(), + token_0_amount + swap_amount + ); + + assert_eq!( + SocialSwap2::reserve1(), + token_1_amount - expected_output_amount + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, TOKEN0), + token_0_amount + swap_amount + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, TOKEN1), + token_1_amount - expected_output_amount + ); + + assert_eq!( + pallet_assets::Module::::balance(ASSET_ID, ACCOUNT2), + pallet_assets::Module::::total_supply(ASSET_ID) + expected_output_amount + - MINIMUM_LIQUIDITY as u128 + ); + }); +} + + +fn add_liquidity(token_0_amount: u128, token_1_amount: u128) { + SocialSwap2::initialize(Origin::root(), FEE_TO, ADDRESS0, TREASURY, TOKEN0, TOKEN1); + pallet_assets::Module::::transfer(&ASSET_ID, &ACCOUNT1, &TOKEN0, token_0_amount); + pallet_assets::Module::::transfer(&ASSET_ID, &ACCOUNT1, &TOKEN1, token_1_amount); + assert_ok!( + SocialSwap2::mint( + Origin::signed(ACCOUNT1), + ACCOUNT2 + ) + ); +} + diff --git a/workers/social-tokens-distributor/README.md b/workers/social-tokens-distributor/README.md new file mode 100644 index 000000000..45a10f944 --- /dev/null +++ b/workers/social-tokens-distributor/README.md @@ -0,0 +1,16 @@ +# social-tokens-distributor + +## Build + +```bash +yarn +tsc +``` + +## Running + +```bash +export WS_URL="ws://127.0.0.1:9944" +export PHRASE="0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a" # Secret seed of //Alice +node dist/index.js +``` diff --git a/workers/social-tokens-distributor/config/types.json b/workers/social-tokens-distributor/config/types.json new file mode 100644 index 000000000..b84e45ff2 --- /dev/null +++ b/workers/social-tokens-distributor/config/types.json @@ -0,0 +1,89 @@ +{ + "AttributeTransaction": { + "signature": "Signature", + "name": "Vec", + "value": "Vec", + "validity": "u32", + "signer": "AccountId", + "identity": "AccountId" + }, + "Attribute": { + "name": "Vec", + "value": "Vec", + "validity": "BlockNumber", + "creation": "Moment", + "nonce": "u64" + }, + "SwapId": "u64", + "Swap": { + "token_id": "TokenId", + "swap_token": "TokenId", + "account": "AccountId" + }, + "AssetId": "u32", + "SocialTokenBalance": "u128", + "RegistrarIndex": "u32", + "Judgement": { + "_enum": [ + "Requested", + "Approved" + ] + }, + "JudgementItem": "(RegistrarIndex, Judgement)", + "Registration": { + "judgements": "Vec", + "account_id": "AccountId" + }, + "Bloom": "H256", + "Log": { + "address": "H160", + "topics": "Vec", + "data": "Bytes" + }, + "Receipt": { + "state_root": "H256", + "used_gas": "U256", + "logs_bloom": "Bloom", + "logs": "Vec" + }, + "TransactionAction": { + "_enum": { + "Call": "H160", + "Create": "Null" + } + }, + "TransactionRecoveryId": "u64", + "TransactionSignature": { + "v": "TransactionRecoveryId", + "r": "H256", + "s": "H256" + }, + "Transaction": { + "nonce": "U256", + "gas_price": "U256", + "gas_limit": "U256", + "action": "TransactionAction", + "value": "U256", + "input": "Bytes", + "signature": "TransactionSignature" + }, + "TransactionStatus": { + "transaction_hash": "H256", + "transaction_index": "u32", + "from": "H160", + "to": "Option", + "contract_address": "Option", + "logs": "Vec", + "logs_bloom": "Bloom" + }, + "Id": "AuthorityId", + "ChainId": "u8", + "ResourceId": "Vec", + "ExchangeId": "u64", + "CurrencyOf": "Balance", + "NftId": "U256", + "Erc721Token": { + "id": "NftId", + "metadata": "Vec" + } +} \ No newline at end of file diff --git a/workers/social-tokens-distributor/package.json b/workers/social-tokens-distributor/package.json new file mode 100644 index 000000000..100df3d50 --- /dev/null +++ b/workers/social-tokens-distributor/package.json @@ -0,0 +1,10 @@ +{ + "name": "social-tokens-distributor", + "version": "0.1.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "@polkadot/api": "^4.7.2", + "@polkadot/types": "^4.7.2" + } +} diff --git a/workers/social-tokens-distributor/src/index.ts b/workers/social-tokens-distributor/src/index.ts new file mode 100644 index 000000000..aee4ca9bc --- /dev/null +++ b/workers/social-tokens-distributor/src/index.ts @@ -0,0 +1,106 @@ +// Required imports +const { ApiPromise, WsProvider } = require("@polkadot/api"); +const { ISubmittableResult, EventRecord } = require("@polkadot/types/types"); +const { EraRewardPoints } = require("@polkadot/types/interfaces/staking"); +const { Keyring, KeyringPair } = require("@polkadot/keyring"); +const { randomAsU8a } = require("@polkadot/util-crypto"); + +const WS_URL = process.env.WS_URL; +const PHRASE = process.env.PHRASE; + +async function main() { + // Initialise the provider to connect to the local node + const provider = new WsProvider(WS_URL); + + const types = require("../config/types.json"); + + // Create the API and wait until ready + const api = await ApiPromise.create({ provider, types }); + + const keyring = new Keyring({ type: "sr25519" }); + const account = keyring.addFromUri(PHRASE); + + // Retrieve the chain & node information information via rpc calls + const [chain, nodeName, nodeVersion] = await Promise.all([ + api.rpc.system.chain(), + api.rpc.system.name(), + api.rpc.system.version(), + ]); + + console.log(`You are connected to chain ${chain} using ${nodeName} v${nodeVersion}`); + + const [historyDepth, currentEra] = await Promise.all([ + api.query.staking.historyDepth(), + api.query.staking.currentEra(), + ]); + + console.log(`Got init data from storage, historyDepth: ${historyDepth}, currentEra: ${currentEra}`); + + let { nonce } = await api.query.system.account(account.address); + for (let era = Math.max(currentEra - historyDepth, 0); era < currentEra; era ++) { + const erasRewardPoints: typeof EraRewardPoints = await api.query.staking.erasRewardPoints(era); + for (const stash in JSON.parse(erasRewardPoints.individual)) { + console.log(`Start processing, era: ${era}, stash: ${stash}, nonce: ${nonce}}`); + await payout_stakers(api, account, era, stash, nonce); + nonce++; + } + } + + sleep(1000); + console.log("Done"); +} + +async function payout_stakers( + api: typeof ApiPromise, + account: typeof KeyringPair, + era: number, + stash: string, + nonce: number, +) { + await api.tx.socialTreasury + .payoutStakers(stash, era) + .signAndSend(account, { nonce }, ({ events = [], status, dispatchError}: typeof ISubmittableResult) => { + console.log("Transaction status:", status.type); + + if (dispatchError) { + if (dispatchError.isModule) { + // for module errors, we have the section indexed, lookup + const decoded = api.registry.findMetaError(dispatchError.asModule); + const { documentation, name, section } = decoded; + console.log(`${section}.${name}: ${documentation.join(" ")}`); + } else { + // Other, CannotLookup, BadOrigin, no extra info + console.log(dispatchError.toString()); + } + } + + if (status.isInBlock) { + console.log("Included at block hash", status.asInBlock.toHex()); + console.log("Events:"); + + events.forEach(({ event: { data, method, section }, phase }: typeof EventRecord) => { + console.log("\t", phase.toString(), `: ${section}.${method}`, data.toString()); + }); + } else if (status.isFinalized) { + console.log("Finalized block hash", status.asFinalized.toHex()); + } + }); +} + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +function check_config() { + if (WS_URL == null) { + console.log("Please set WS_URL"); + process.exit(1); + } + if (PHRASE == null) { + console.log("Please set PHRASE"); + process.exit(1); + } +} + +check_config(); +main().catch(console.error).finally(() => process.exit()); diff --git a/workers/social-tokens-distributor/tsconfig.json b/workers/social-tokens-distributor/tsconfig.json new file mode 100644 index 000000000..ca2128200 --- /dev/null +++ b/workers/social-tokens-distributor/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "outDir": "dist", + "sourceMap": true, + "strict": true, + "target": "es2017" + }, + "include": [ + "src" + ] +} diff --git a/workers/social-tokens-distributor/yarn.lock b/workers/social-tokens-distributor/yarn.lock new file mode 100644 index 000000000..83f333274 --- /dev/null +++ b/workers/social-tokens-distributor/yarn.lock @@ -0,0 +1,637 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime@^7.13.17", "@babel/runtime@^7.13.9": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" + integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== + dependencies: + regenerator-runtime "^0.13.4" + +"@polkadot/api-derive@4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-4.7.2.tgz#94ca16f2e61d1739489da6030cbe5137beee336a" + integrity sha512-TRctRtGFvsso5R/dGwna2QiaQy14NFhrthu/sLh3oYIMNwQ6BeGHhbfjAi0pidc+ID2ia6LDLRw7vl/LVSuHIA== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/api" "4.7.2" + "@polkadot/rpc-core" "4.7.2" + "@polkadot/types" "4.7.2" + "@polkadot/util" "^6.3.1" + "@polkadot/util-crypto" "^6.3.1" + "@polkadot/x-rxjs" "^6.3.1" + bn.js "^4.11.9" + +"@polkadot/api@4.7.2", "@polkadot/api@^4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-4.7.2.tgz#0e98117fc631f2988ca364881111c05a00e8b701" + integrity sha512-PkWrgx+gICWt5uYjYoP68a8jyCRBkQXn9ecr0ReVr9jiiwvBYZyw3CLXTVRwJ89PpJLtDVz9QGCa4dM6j4a/LA== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/api-derive" "4.7.2" + "@polkadot/keyring" "^6.3.1" + "@polkadot/metadata" "4.7.2" + "@polkadot/rpc-core" "4.7.2" + "@polkadot/rpc-provider" "4.7.2" + "@polkadot/types" "4.7.2" + "@polkadot/types-known" "4.7.2" + "@polkadot/util" "^6.3.1" + "@polkadot/util-crypto" "^6.3.1" + "@polkadot/x-rxjs" "^6.3.1" + bn.js "^4.11.9" + eventemitter3 "^4.0.7" + +"@polkadot/keyring@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-6.3.1.tgz#434847cc4fb134116691c07e05750e8388cbb2b7" + integrity sha512-uVWhdd4TVtLc4R2OtiKHJE5jgJZnuEognbgjl5RT2uKrCJYTsYnq0IeRTvMmtdPJAJvGeD3JTsX2ekgt3tJpuw== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/util" "6.3.1" + "@polkadot/util-crypto" "6.3.1" + +"@polkadot/metadata@4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-4.7.2.tgz#91076a1c1158494574a1965ec1fe124fb4d1b848" + integrity sha512-+YWeD828cpIpD31epoSZfK5kmtJsBcgTTyYsQ4bBO4NBJceLTipmHgyLXs7fXjU4cC6uvSuBnAV0KGJW7L9nDQ== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/types" "4.7.2" + "@polkadot/types-known" "4.7.2" + "@polkadot/util" "^6.3.1" + "@polkadot/util-crypto" "^6.3.1" + bn.js "^4.11.9" + +"@polkadot/networks@6.3.1", "@polkadot/networks@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-6.3.1.tgz#c5063681ea73f8b579f418d57d0eba2d4bb72292" + integrity sha512-oANup0CLGt75CPbE3gz2HUWUlqQKucImdb1TtStLXMUH+Aj8ZOnQFA2lwixzaRdx+ymPfmEL7GkF36i96OqQVw== + dependencies: + "@babel/runtime" "^7.13.17" + +"@polkadot/rpc-core@4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-4.7.2.tgz#d79c42bb9fafc84a5c3b90959a86fe97b4a21fb5" + integrity sha512-LxpI8TVdjL99nfnLQog1tL0STdiYIUSLzdN+2PKvaO0YoBPAau945PVfz6GeN7wqQZKxR6N/1SKhAVfYcP7ctA== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/metadata" "4.7.2" + "@polkadot/rpc-provider" "4.7.2" + "@polkadot/types" "4.7.2" + "@polkadot/util" "^6.3.1" + "@polkadot/x-rxjs" "^6.3.1" + +"@polkadot/rpc-provider@4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-4.7.2.tgz#71340ec6a109a2652502aee8d24e06f0322663b8" + integrity sha512-nvKScoPTzi7PFA5XnVI1lbebLjdwF6OcvWC0QvaEUrbwoG3NPFtoAGF6r2HloX0Q/Qo6FEHowrX0nY4gTM+QCw== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/types" "4.7.2" + "@polkadot/util" "^6.3.1" + "@polkadot/util-crypto" "^6.3.1" + "@polkadot/x-fetch" "^6.3.1" + "@polkadot/x-global" "^6.3.1" + "@polkadot/x-ws" "^6.3.1" + bn.js "^4.11.9" + eventemitter3 "^4.0.7" + +"@polkadot/types-known@4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-4.7.2.tgz#d6b55c75a9461a09490167a679b6ed783eab6350" + integrity sha512-vlIMBtBdVYHyjWqonQ39PCdZJ9WeTsA5h5zr2mEpkCSoopby5pZ6eOR8CPqjc1uYbdDHT72Dbtx8Wr9L7z8HXg== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/networks" "^6.3.1" + "@polkadot/types" "4.7.2" + "@polkadot/util" "^6.3.1" + bn.js "^4.11.9" + +"@polkadot/types@4.7.2", "@polkadot/types@^4.7.2": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-4.7.2.tgz#6b3e8c16999951e040459d5e338e259543bfec16" + integrity sha512-8qj/f6YAv5XCDLej60uWlX+cjuz2qt6AJKemi+lozA4vYZ9XTXIODYLv/XfjimbVABzeLdIAwWX1OQO9+F0Gng== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/metadata" "4.7.2" + "@polkadot/util" "^6.3.1" + "@polkadot/util-crypto" "^6.3.1" + "@polkadot/x-rxjs" "^6.3.1" + "@types/bn.js" "^4.11.6" + bn.js "^4.11.9" + +"@polkadot/util-crypto@6.3.1", "@polkadot/util-crypto@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-6.3.1.tgz#5328da77bdee5064bc41f9dec0a76cc634690b88" + integrity sha512-fwH4t6EN2XACwJB2Z5xUyNo4mQ1RXJj0MgVaaLua8PbG0qq9tt4eaEbdVzrm7A6igIfsTntDoZISTfVjBcRtkQ== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/networks" "6.3.1" + "@polkadot/util" "6.3.1" + "@polkadot/wasm-crypto" "^4.0.2" + "@polkadot/x-randomvalues" "6.3.1" + base-x "^3.0.8" + base64-js "^1.5.1" + blakejs "^1.1.0" + bn.js "^4.11.9" + create-hash "^1.2.0" + elliptic "^6.5.4" + hash.js "^1.1.7" + js-sha3 "^0.8.0" + scryptsy "^2.1.0" + tweetnacl "^1.0.3" + xxhashjs "^0.2.2" + +"@polkadot/util@6.3.1", "@polkadot/util@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-6.3.1.tgz#410ee362ddb37f9c67af8f5897d977a7fd950ebf" + integrity sha512-M9pGaXSB67DZPckdNQU29wq5W7BUOh6qeu5LonzxpUek+riJfbiF9JOgZQ2Q/aEFYbd1hqLbOMsLRZLhSmlbYw== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/x-textdecoder" "6.3.1" + "@polkadot/x-textencoder" "6.3.1" + "@types/bn.js" "^4.11.6" + bn.js "^4.11.9" + camelcase "^5.3.1" + ip-regex "^4.3.0" + +"@polkadot/wasm-crypto-asmjs@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-4.0.2.tgz#f42c353a64e1243841daf90e4bd54eff01a4e3cf" + integrity sha512-hlebqtGvfjg2ZNm4scwBGVHwOwfUhy2yw5RBHmPwkccUif3sIy4SAzstpcVBIVMdAEvo746bPWEInA8zJRcgJA== + dependencies: + "@babel/runtime" "^7.13.9" + +"@polkadot/wasm-crypto-wasm@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-4.0.2.tgz#89f9e0a1e4d076784d4a42bea37fc8b06bdd8bb6" + integrity sha512-de/AfNPZ0uDKFWzOZ1rJCtaUbakGN29ks6IRYu6HZTRg7+RtqvE1rIkxabBvYgQVHIesmNwvEA9DlIkS6hYRFQ== + dependencies: + "@babel/runtime" "^7.13.9" + +"@polkadot/wasm-crypto@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-4.0.2.tgz#9649057adee8383cc86433d107ba526b718c5a3b" + integrity sha512-2h9FuQFkBc+B3TwSapt6LtyPvgtd0Hq9QsHW8g8FrmKBFRiiFKYRpfJKHCk0aCZzuRf9h95bQl/X6IXAIWF2ng== + dependencies: + "@babel/runtime" "^7.13.9" + "@polkadot/wasm-crypto-asmjs" "^4.0.2" + "@polkadot/wasm-crypto-wasm" "^4.0.2" + +"@polkadot/x-fetch@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-6.3.1.tgz#ac1737f57a2a03b6666aec6abe3c8a2e147c2696" + integrity sha512-goBtKZarq5sXV2G98inj2v1ivVNF9gif8sg6IqsGRbljca6K6pZWTVd0yGWe7OABnCkFQotk283nly9nkr9+1g== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/x-global" "6.3.1" + "@types/node-fetch" "^2.5.10" + node-fetch "^2.6.1" + +"@polkadot/x-global@6.3.1", "@polkadot/x-global@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-6.3.1.tgz#cdb4883fa20e23411bdd5f50a5d5c92814a3106f" + integrity sha512-eFooGQdxJpiOsm3AKTSMInaecBKaQ/tqOUJNm/CpdJalCqTDMp/qzgj64Uflk9eUqGgk7jB7Q5FaQdyWsC0Mtg== + dependencies: + "@babel/runtime" "^7.13.17" + "@types/node-fetch" "^2.5.10" + node-fetch "^2.6.1" + +"@polkadot/x-randomvalues@6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-6.3.1.tgz#e2b91223277d7d7978c39e9d280fbc6526217d46" + integrity sha512-SZ5MUYm1fd1fgGFexMWbbG8zZgCS7b9QNKaIcnv1Dwlfp2meDoDlgoedn+1pCJ6VEa1adswqLHX4WbYA4D9ynA== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/x-global" "6.3.1" + +"@polkadot/x-rxjs@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-rxjs/-/x-rxjs-6.3.1.tgz#5627f9601df6db22a65512a3eab0af4a22a58830" + integrity sha512-Z9mbvpixr0fopQh049tFlR8r/RItOyYRL4P7YqwnfeROqxU4R8UTmmB8As9y/zy0O5Jlkjzy9MdyQgwzhGQOcQ== + dependencies: + "@babel/runtime" "^7.13.17" + rxjs "^6.6.7" + +"@polkadot/x-textdecoder@6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-6.3.1.tgz#ab0eec87d5df2d119480fa7a3657d8d72f583af8" + integrity sha512-lLb11yaAmyx2STw7ZmdgPtV7LI26U/5h1K527cM7QnxgTQgYggtAt4f9aLHiWsmOCvnT0U0PWsWSUbAJrLHLBA== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/x-global" "6.3.1" + +"@polkadot/x-textencoder@6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-6.3.1.tgz#2277770650f5637698d7d8cd7ac0cfd5ca0dace2" + integrity sha512-7V5OuT43JPTm7rrwdBEMzXAF5nLg+t6q24ntZHNcFUH1pdkP/+2f3vGM3e9BK5k4wkQLoepod5gyY6Qbw9bsYQ== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/x-global" "6.3.1" + +"@polkadot/x-ws@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-6.3.1.tgz#1534f8d1cd03dbf497410725d14313e5554a2ffd" + integrity sha512-bDb9a+bxoaNOza0EeLp9M6FKYz9ogJcFQzRP+YR6ND7oQ0QcQG06XloRKTU0wtcZRKP8AzkYYN+FAc/6bnIqTw== + dependencies: + "@babel/runtime" "^7.13.17" + "@polkadot/x-global" "6.3.1" + "@types/websocket" "^1.0.2" + websocket "^1.0.34" + +"@types/bn.js@^4.11.6": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/node-fetch@^2.5.10": + version "2.5.10" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" + integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "15.0.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.1.tgz#ef34dea0881028d11398be5bf4e856743e3dc35a" + integrity sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA== + +"@types/websocket@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.2.tgz#d2855c6a312b7da73ed16ba6781815bf30c6187a" + integrity sha512-B5m9aq7cbbD/5/jThEr33nUY8WEfVi6A2YKCTOvw5Ldy7mtsOkqRvGjnzy6g7iMMDsgu7xREuCzqATLDLQVKcQ== + dependencies: + "@types/node" "*" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= + +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +bufferutil@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" + integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== + dependencies: + node-gyp-build "^4.2.0" + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +cipher-base@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +cuint@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" + integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs= + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ip-regex@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mime-db@1.47.0: + version "1.47.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" + integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== + +mime-types@^2.1.12: + version "2.1.30" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" + integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== + dependencies: + mime-db "1.47.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rxjs@^6.6.7: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +scryptsy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" + integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== + +sha.js@^2.4.0: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +utf-8-validate@^5.0.2: + version "5.0.4" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.4.tgz#72a1735983ddf7a05a43a9c6b67c5ce1c910f9b8" + integrity sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q== + dependencies: + node-gyp-build "^4.2.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +websocket@^1.0.34: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +xxhashjs@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8" + integrity sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw== + dependencies: + cuint "^0.2.2" + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=