├── Cargo.toml ├── .gitignore ├── programs └── nft-staking │ ├── Xargo.toml │ ├── Cargo.toml │ └── src │ ├── utils.rs │ └── lib.rs ├── Anchor.toml ├── tsconfig.json ├── README.md ├── package.json ├── client ├── utils.ts ├── pool.ts ├── nft_staking.json ├── main.ts └── token_mints.json └── Cargo.lock /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "programs/*" 4 | ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | client/keys 3 | deploy 4 | client/token_test_mints.json -------------------------------------------------------------------------------- /programs/nft-staking/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] -------------------------------------------------------------------------------- /Anchor.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["programs/nft-staking"] 3 | 4 | [registry] 5 | url = "https://anchor.projectserum.com" 6 | 7 | [provider] 8 | cluster = "mainnet" 9 | wallet = "/home/plmoknij/Desktop/ParamsNFT/staking_keys/staking_key_20211222.json" 10 | 11 | [programs.localnet] 12 | nft_staking = "paramKFFuRPLVXZWjDRbnk5xKemduYZUW2BqUp7xZD3" 13 | 14 | [scripts] 15 | test = "ts-mocha --path tsconfig.json -t 1000000 tests/nft-staking/*.ts" 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": [ 4 | "mocha", 5 | "chai" 6 | ], 7 | "typeRoots": [ 8 | "./node_modules/@types" 9 | ], 10 | "lib": [ 11 | "es2015" 12 | ], 13 | "module": "commonjs", 14 | "target": "es5", 15 | "esModuleInterop": true, 16 | "allowSyntheticDefaultImports": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "strict": false, 19 | "skipLibCheck": true 20 | } 21 | } -------------------------------------------------------------------------------- /programs/nft-staking/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nft_staking" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | crate-type = ["cdylib", "lib"] 10 | name = "nft_staking" 11 | 12 | [features] 13 | no-entrypoint = [] 14 | no-idl = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | 18 | #add to default for local testing 19 | #default = ["local-testing"] 20 | local-testing = [] 21 | 22 | [dependencies] 23 | anchor-lang = "0.17.0" 24 | anchor-spl = "0.17.0" 25 | arrayref = "0.3.6" 26 | spl-token = { version = "3.1.1", features = ["no-entrypoint"] } 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFT staking 2 | - pays out constant reward rate per token 3 | 4 | *note manually added mintAddresses in Config fields in the anchor IDL file 5 | 6 | 7 | # Solana Program using Anchor - Staking 8 | 9 | ## Anchor 10 | 11 | ### Build 12 | 13 | ```sh 14 | anchor build 15 | ``` 16 | 17 | ### Deploy 18 | 19 | ```sh 20 | solana program deploy --programid target/verifiable/nft_staking.so 21 | ``` 22 | 23 | ```sh 24 | anchor test 25 | ``` 26 | 27 | ## configure CLI 28 | 29 | ### Set CLI config url to localhost cluster 30 | 31 | ```sh 32 | solana config set --url localhost 33 | ``` 34 | 35 | ### Create CLI Keypair 36 | 37 | ```sh 38 | solana-keygen new -o "./test-key.json" 39 | ``` 40 | 41 | ## Start local Solana cluster 42 | 43 | ### Start a local Solana cluster 44 | 45 | ```sh 46 | solana-test-validator 47 | ``` 48 | 49 | ### Listen to transaction logs 50 | 51 | ```sh 52 | solana logs 53 | ``` 54 | 55 | ## Build the on-chain program 56 | 57 | ```sh 58 | npm run build:anchor 59 | ``` 60 | 61 | ## Deploy the on-chain program 62 | 63 | ```sh 64 | solana program deploy target/deploy/nft_staking.so 65 | ``` 66 | 67 | ## Run js client 68 | 69 | ```sh 70 | npm run start 71 | ``` 72 | 73 | ### create own token 74 | ```sh 75 | spl-token create-token 76 | ``` 77 | 78 | ### supply 79 | ```sh 80 | spl-token supply EHkcswrrUs3Ej3gFYCmjmAMwSQC8j1cQtJCFEEzUYrpa 81 | ``` 82 | 83 | ### create token account 84 | ```sh 85 | spl-token create-account EHkcswrrUs3Ej3gFYCmjmAMwSQC8j1cQtJCFEEzUYrpa 86 | ``` 87 | 88 | ### mint token 89 | ```sh 90 | spl-token mint EHkcswrrUs3Ej3gFYCmjmAMwSQC8j1cQtJCFEEzUYrpa 1000000000000 91 | ``` 92 | -------------------------------------------------------------------------------- /programs/nft-staking/src/utils.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | use anchor_lang::solana_program::{clock}; 3 | use std::convert::TryInto; 4 | use crate::{Pool, User}; 5 | 6 | const PRECISION: u128 = u64::MAX as u128; 7 | 8 | // update user pending reward, update user last update time 9 | #[inline(always)] 10 | pub fn update_rewards( 11 | pool: &mut ProgramAccount, 12 | user: Option<&mut ProgramAccount>, 13 | ) -> ProgramResult { 14 | let clock = clock::Clock::get().unwrap(); 15 | 16 | let now = clock.unix_timestamp.try_into().unwrap(); 17 | 18 | if let Some(u) = user { 19 | // calculate time elapsed since last update 20 | let time_diff = std::cmp::max(now - u.last_update_time, 0 as u64); 21 | // update user reward to pass it to pending reward 22 | u.reward_earned_pending = earned( 23 | time_diff, 24 | u.mint_staked_count, 25 | pool.reward_rate_per_token, 26 | u.reward_earned_pending, 27 | ); 28 | // update time in user account 29 | u.last_update_time = now; 30 | } 31 | Ok(()) 32 | } 33 | 34 | #[inline(always)] 35 | pub fn earned( 36 | elapsed_time: u64, 37 | balance_staked: u32, 38 | reward_rate_per_token: u128, 39 | user_reward_per_token_pending: u64, 40 | ) -> u64 { 41 | /* 42 | earned reward = (now - last_update_time) * reward rate * balance_staked + user_rewards_x_pending 43 | returns new pending rewards 44 | */ 45 | let earned = (reward_rate_per_token as u128) 46 | .checked_div(PRECISION) 47 | .unwrap() 48 | .checked_mul(balance_staked as u128) 49 | .unwrap() 50 | .checked_mul(elapsed_time as u128) 51 | .unwrap() 52 | .checked_add(user_reward_per_token_pending as u128) 53 | .unwrap() 54 | .try_into() 55 | .unwrap(); 56 | return earned; 57 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solana-anchor-staking", 3 | "version": "0.1.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "ts-node client/main.ts", 7 | "start:pool": "ts-node client/pool.ts", 8 | "start:de": "ts-node client/de.ts", 9 | "start-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health start", 10 | "lint": "eslint --ext .ts client/* && prettier --check \"client/**/*.ts\"", 11 | "lint:fix": "eslint --ext .ts client/* --fix && prettier --write \"client/**/*.ts\"", 12 | "clean": "npm run clean:program-c && npm run clean:program-rust", 13 | "build:anchor": "anchor build", 14 | "clean:anchor": "rm -rf ./.anchor && rm -rf ./target", 15 | "deploy:anchor": "ts-node migrations/deploy.ts", 16 | "test:anchor": "anchor test" 17 | }, 18 | "dependencies": { 19 | "@project-serum/anchor": "^0.17.1-beta.1", 20 | "@project-serum/borsh": "^0.2.2", 21 | "@project-serum/common": "^0.0.1-beta.3", 22 | "@project-serum/serum": "^0.13.60", 23 | "@solana/spl-token": "^0.1.8", 24 | "@solana/web3.js": "^1.30.0", 25 | "@types/chai": "^4.2.22", 26 | "@types/mocha": "^9.0.0", 27 | "@types/node": "^16.11.1", 28 | "axios": "^0.22.0", 29 | "b58": "^4.0.3", 30 | "borsh": "^0.6.0", 31 | "chai": "^4.3.4", 32 | "fs": "^0.0.1-security", 33 | "mocha": "^8.4.0", 34 | "mz": "^2.7.0", 35 | "pako": "^2.0.4", 36 | "plotly": "^1.0.6", 37 | "plotly.js": "^2.5.1", 38 | "plotly.js-dist-min": "^2.5.1", 39 | "react-plotly.js": "^2.5.1", 40 | "ts-mocha": "^8.0.0", 41 | "ts-node": "^10.3.0", 42 | "typescript": "^4.4.4", 43 | "yaml": "^1.10.2" 44 | }, 45 | "devDependencies": { 46 | "@tsconfig/recommended": "^1.0.1", 47 | "@types/eslint": "^7.2.4", 48 | "@types/eslint-plugin-prettier": "^3.1.0", 49 | "@types/mz": "^2.7.2", 50 | "@types/prettier": "^2.1.5", 51 | "@types/yaml": "^1.9.7", 52 | "@typescript-eslint/eslint-plugin": "^4.6.0", 53 | "@typescript-eslint/parser": "^4.6.0", 54 | "eslint": "^7.12.1", 55 | "eslint-config-prettier": "^6.15.0", 56 | "eslint-plugin-prettier": "^4.0.0", 57 | "prettier": "^2.1.2", 58 | "start-server-and-test": "^1.11.6", 59 | "ts-node": "^10.0.0", 60 | "typescript": "^4.0.5" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /client/utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 3 | /* eslint-disable @typescript-eslint/no-unsafe-return */ 4 | 5 | import os from 'os'; 6 | import fs from 'mz/fs'; 7 | import path from 'path'; 8 | import yaml from 'yaml'; 9 | import {Keypair} from '@solana/web3.js'; 10 | 11 | /** 12 | * @private 13 | */ 14 | async function getConfig(): Promise { 15 | // Path to Solana CLI config file 16 | const CONFIG_FILE_PATH = path.resolve( 17 | os.homedir(), 18 | '.config', 19 | 'solana', 20 | 'cli', 21 | 'config.yml', 22 | ); 23 | const configYml = await fs.readFile(CONFIG_FILE_PATH, {encoding: 'utf8'}); 24 | return yaml.parse(configYml); 25 | } 26 | 27 | /** 28 | * Load and parse the Solana CLI config file to determine which RPC url to use 29 | */ 30 | export async function getRpcUrl(): Promise { 31 | try { 32 | const config = await getConfig(); 33 | if (!config.json_rpc_url) throw new Error('Missing RPC URL'); 34 | return config.json_rpc_url; 35 | } catch (err) { 36 | console.warn( 37 | 'Failed to read RPC url from CLI config file, falling back to localhost', 38 | ); 39 | return 'http://localhost:8899'; 40 | } 41 | } 42 | 43 | /** 44 | * If the lamports are in your account, you need to sign the transaction with your private key so no one else can spend your lamports. 45 | * This private key is stored in your local filesystem as an array of bytes. 46 | * The createKeypairFromFile function decodes this array and returns it as a Keypair using the fromSecretKey method provided to us by the JSON rpc API. 47 | * The getPayer function returns a Keypair that is debited everytime we make a transaction. 48 | */ 49 | /** 50 | * Load and parse the Solana CLI config file to determine which payer to use 51 | */ 52 | export async function getPayer(): Promise { 53 | try { 54 | const config = await getConfig(); 55 | if (!config.keypair_path) throw new Error('Missing keypair path'); 56 | return await createKeypairFromFile(config.keypair_path); 57 | } catch (err) { 58 | console.warn( 59 | 'Failed to create keypair from CLI config file, falling back to new random keypair', 60 | ); 61 | return Keypair.generate(); 62 | } 63 | } 64 | 65 | /** 66 | * Create a Keypair from a secret key stored in file as bytes' array 67 | */ 68 | export async function createKeypairFromFile( 69 | filePath: string, 70 | ): Promise { 71 | const secretKeyString = await fs.readFile(filePath, {encoding: 'utf8'}); 72 | const secretKey = Uint8Array.from(JSON.parse(secretKeyString)); 73 | return Keypair.fromSecretKey(secretKey); 74 | } 75 | 76 | /** 77 | * In Solana, we also have to pay rent for the storage cost of keeping the account alive. 78 | * However, an account can be made entirely exempt from rent collection by depositing at least 2 years worth of rent. 79 | * The getMinimumBalanceForRentExemption API can be used to get the minimum balance required for a particular account. 80 | */ -------------------------------------------------------------------------------- /client/pool.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This script is for pool initialization and activities 3 | */ 4 | 5 | import * as anchor from "@project-serum/anchor"; 6 | import {readFileSync} from "fs"; 7 | import {PublicKey} from "@solana/web3.js"; 8 | import {TOKEN_PROGRAM_ID} from "@solana/spl-token"; 9 | import {getTokenAccount} from "@project-serum/common"; 10 | import {CreateAccountWithSeedParams} from "@solana/web3.js"; 11 | import {sendAndConfirmTransaction} from "@solana/web3.js"; 12 | 13 | export const STAKE_PROGRAM = new anchor.web3.PublicKey( 14 | "paramKFFuRPLVXZWjDRbnk5xKemduYZUW2BqUp7xZD3" 15 | ); 16 | 17 | //reward token mint 18 | export const WILD_TOKEN = new anchor.web3.PublicKey( 19 | "FpXFK3tZAWNb9eQMHNFpnWH1gQ1ssmSoz7av2VYcHscw" 20 | ) 21 | //reward token account 22 | export const WILD_VAULT = new anchor.web3.PublicKey( 23 | "ABGFm7tjupN9B2LSxZLvRHLWRkjdpqBF1kzfC7qQ27wZ" 24 | ) 25 | 26 | const REWARD_DURATION = 31536000; 27 | export const NFT_ADDRESSES = JSON.parse(require('fs').readFileSync('./client/token_mints.json', 'utf8')); 28 | export const NUM_NFT = NFT_ADDRESSES.length; 29 | const idl = JSON.parse(require('fs').readFileSync('./client/nft_staking.json', 'utf8')); 30 | 31 | const keys_local = "" 32 | 33 | const ENV = "mainnet-beta"; 34 | 35 | 36 | const PREFIX = "nft_staking"; 37 | const PREFIX_USER = "nft_staking_user" 38 | const PREFIX_MINT = "nft_staking_mint" 39 | 40 | export interface Pool { 41 | id: anchor.web3.PublicKey, 42 | connection: anchor.web3.Connection; 43 | program: anchor.Program; 44 | } 45 | 46 | interface PoolState { 47 | isInitialized: boolean; 48 | authority: anchor.web3.PublicKey; 49 | paused: boolean; 50 | config: anchor.web3.PublicKey; 51 | rewardMint: anchor.web3.PublicKey; 52 | rewardVault: anchor.web3.PublicKey; 53 | // rewardRatePerToken: u128 // how to conver it to number 54 | lastUpdateTime: number, 55 | rewardDuration: number, 56 | rewardDurationEnd: number, 57 | tokenStakeCount: number, 58 | userCount: number 59 | } 60 | 61 | interface ConfigState { 62 | authority: anchor.web3.PublicKey; 63 | uuid: string; 64 | numMint: number, 65 | mintAddresses: anchor.web3.PublicKey[]; 66 | } 67 | 68 | export const getPoolState = async ( 69 | program: anchor.Program, 70 | poolAccount: anchor.web3.PublicKey, // pool account public key 71 | ): Promise => { 72 | let state = await program.account.pool.fetch(poolAccount); 73 | 74 | if (state == null) { 75 | return null; 76 | } 77 | console.log(state) 78 | let isInitialized = state.isInitialized; 79 | let authority = state.authority; 80 | let paused = state.paused; 81 | let config = state.config; 82 | let rewardMint = state.rewardMint 83 | let rewardVault = state.rewardVault 84 | let lastUpdateTime = state.lastUpdateTime.toNumber() 85 | let rewardDuration = state.rewardDuration.toNumber(); 86 | let rewardDurationEnd = state.rewardDurationEnd.toNumber(); 87 | let tokenStakeCount = state.tokenStakeCount; 88 | let userCount = state.userCount; 89 | return { 90 | isInitialized, 91 | authority, 92 | paused, 93 | config, 94 | rewardMint, 95 | rewardVault, 96 | lastUpdateTime, 97 | rewardDuration, 98 | rewardDurationEnd, 99 | tokenStakeCount, 100 | userCount 101 | } 102 | } 103 | 104 | export const getConfigState = async ( 105 | program: anchor.Program, 106 | configAccount: anchor.web3.PublicKey, // pool account public key 107 | ): Promise => { 108 | let state = await program.account.config.fetch(configAccount); 109 | 110 | if (state == null) { 111 | return null; 112 | } 113 | let authority = state.authority; 114 | let uuid = state.uuid; 115 | let numMint = state.numMint; 116 | let mintAddresses = state.mintAddresses.map((element) => element) 117 | return { 118 | authority, 119 | uuid, 120 | numMint, 121 | mintAddresses 122 | } 123 | } 124 | 125 | const generateUuid = (): string => { 126 | return anchor.web3.Keypair.generate() 127 | .publicKey.toBase58() 128 | .slice(0, 6); 129 | } 130 | 131 | // derive a config account that falls off the ed25519 curve 132 | const getConfigAccount = async ( 133 | authority: anchor.web3.PublicKey, 134 | ): Promise<[anchor.web3.PublicKey, string]> => { 135 | 136 | let configUuid = generateUuid() 137 | 138 | let configAccount = await PublicKey.createWithSeed( 139 | authority, 140 | configUuid, 141 | STAKE_PROGRAM, 142 | ); 143 | 144 | while (PublicKey.isOnCurve(configAccount.toBuffer())) { 145 | configUuid = generateUuid() 146 | configAccount = await PublicKey.createWithSeed( 147 | authority, 148 | configUuid, 149 | STAKE_PROGRAM, 150 | ); 151 | } 152 | 153 | return [configAccount, configUuid] 154 | }; 155 | 156 | const getPoolAccount = async ( 157 | authority: anchor.web3.PublicKey, 158 | configPubKey: anchor.web3.PublicKey, 159 | ): Promise<[anchor.web3.PublicKey, number]> => { 160 | return ( 161 | await anchor.web3.PublicKey.findProgramAddress( 162 | [ 163 | Buffer.from(anchor.utils.bytes.utf8.encode(PREFIX)), 164 | authority.toBuffer(), 165 | configPubKey.toBuffer() 166 | ], 167 | STAKE_PROGRAM 168 | ) 169 | ); 170 | }; 171 | 172 | const getRewardAccount = async ( 173 | authority: anchor.web3.PublicKey, 174 | poolAccount: anchor.web3.PublicKey, 175 | rewardMintId: anchor.web3.PublicKey, 176 | ): Promise<[anchor.web3.PublicKey, number]> => { 177 | return ( 178 | await anchor.web3.PublicKey.findProgramAddress( 179 | [Buffer.from(anchor.utils.bytes.utf8.encode(PREFIX)), 180 | poolAccount.toBuffer(), 181 | authority.toBuffer(), 182 | rewardMintId.toBuffer(),], 183 | STAKE_PROGRAM 184 | ) 185 | ); 186 | }; 187 | 188 | export const initializePool = async ( 189 | program: anchor.Program, 190 | authority: anchor.web3.Keypair, 191 | rewardMint: anchor.web3.PublicKey, 192 | numNFT: number, 193 | rewardDuration: number, 194 | connection: anchor.web3.Connection, 195 | configAccount: anchor.web3.PublicKey, 196 | configUuid: string, 197 | poolAccount: anchor.web3.PublicKey, 198 | poolBump: number, 199 | rewardAccount: anchor.web3.PublicKey, 200 | rewardBump: number 201 | ): Promise => { 202 | 203 | let configSpace = (8 + // discriminator 204 | 32 + // authority 205 | 4 + 6 + // uuid + u32 le 206 | 4 + // num_mint 207 | 4) // u32 len for Vec 208 | + 209 | (32 * numNFT) 210 | 211 | let initPoolTx = program.transaction.initializePool( 212 | poolBump, 213 | configUuid, 214 | new anchor.BN(numNFT), 215 | rewardBump, 216 | new anchor.BN(rewardDuration), 217 | { 218 | accounts: { 219 | authority: authority.publicKey, // owner wallet 220 | poolAccount: poolAccount, // Pool Account 221 | config: configAccount, 222 | rewardMint: rewardMint, 223 | rewardVault: rewardAccount, 224 | rent: anchor.web3.SYSVAR_RENT_PUBKEY, 225 | tokenProgram: TOKEN_PROGRAM_ID, 226 | systemProgram: anchor.web3.SystemProgram.programId, 227 | }, 228 | instructions: [ 229 | anchor.web3.SystemProgram.createAccountWithSeed({ 230 | fromPubkey: authority.publicKey, 231 | newAccountPubkey: configAccount, 232 | basePubkey: authority.publicKey, 233 | seed: configUuid, 234 | lamports: await connection.getMinimumBalanceForRentExemption( 235 | configSpace 236 | ), 237 | space: configSpace, 238 | programId: STAKE_PROGRAM, 239 | }) 240 | ] 241 | 242 | } 243 | ) 244 | console.log(initPoolTx) 245 | let transactions = new anchor.web3.Transaction().add(initPoolTx) 246 | return await sendAndConfirmTransaction( 247 | connection, 248 | transactions, 249 | [authority] 250 | ); 251 | 252 | } 253 | 254 | export const resumePool = async ( 255 | program: anchor.Program, 256 | authority: anchor.web3.Keypair, 257 | poolAccount: anchor.web3.PublicKey, // pool account public key 258 | ): Promise => { 259 | // set pool paused to false 260 | return await program.rpc.resume( 261 | { 262 | accounts: { 263 | authority: authority.publicKey, // owner wallet 264 | poolAccount: poolAccount, // Pool Account 265 | }, 266 | signers: [authority] 267 | } 268 | ); 269 | } 270 | 271 | export const authorizeFunder = async ( 272 | program: anchor.Program, 273 | authority: anchor.web3.Keypair, 274 | poolAccount: anchor.web3.PublicKey, // pool account public key 275 | funder: anchor.web3.PublicKey, // pool account public key 276 | ): Promise => { 277 | return await program.rpc.authorizeFunder( 278 | funder, 279 | { 280 | accounts: { 281 | authority: authority.publicKey, 282 | poolAccount: poolAccount, 283 | }, 284 | signers: [authority], 285 | }); 286 | } 287 | 288 | export const fund = async ( 289 | program: anchor.Program, 290 | funder: anchor.web3.Keypair, 291 | poolAccount: anchor.web3.PublicKey, // pool account public key 292 | configAccount: anchor.web3.PublicKey, // config account public key 293 | rewardVault: anchor.web3.PublicKey, // reward vault 294 | funderVault: anchor.web3.PublicKey, // funder vault 295 | authority: anchor.web3.PublicKey, // authority 296 | amount: number, 297 | ): Promise => { 298 | return await program.rpc.fund( 299 | new anchor.BN(amount), 300 | { 301 | accounts: { 302 | funder: funder.publicKey, 303 | poolAccount: poolAccount, 304 | rewardVault: rewardVault, 305 | funderVault: funderVault, 306 | authority: authority, 307 | tokenProgram: TOKEN_PROGRAM_ID, 308 | config: configAccount 309 | }, 310 | signers: [funder], 311 | }); 312 | } 313 | 314 | export const addMintAddresses = async ( 315 | program: anchor.Program, 316 | authority: anchor.web3.Keypair, 317 | poolAccount: anchor.web3.PublicKey, // pool account public key 318 | configAccount: anchor.web3.PublicKey, // config public key 319 | mintAddresses: string[], 320 | ) => { 321 | let batchSize = 20 322 | // do small batch with 10 in each 323 | if (mintAddresses.length < batchSize) { 324 | await program.rpc.addMintAddresses( 325 | mintAddresses.map((element) => new anchor.web3.PublicKey(element)), 326 | 0, 327 | { 328 | accounts: { 329 | authority: authority.publicKey, // owner wallet 330 | poolAccount: poolAccount, // Pool Account 331 | config: configAccount, // config account 332 | }, 333 | signers: [authority] 334 | } 335 | ); 336 | } else { 337 | let start = 0 338 | do { 339 | let mintAddressesBatch = mintAddresses.slice(start, start + batchSize); 340 | console.log("adding mintAddress:", start) 341 | // console.log(mintAddressesBatch.map((element) => new anchor.web3.PublicKey(element))) 342 | await program.rpc.addMintAddresses( 343 | mintAddressesBatch.map((element) => new anchor.web3.PublicKey(element)), 344 | start, 345 | { 346 | accounts: { 347 | authority: authority.publicKey, // owner wallet 348 | poolAccount: poolAccount, // Pool Account 349 | config: configAccount, // config account 350 | }, 351 | signers: [authority] 352 | } 353 | ); 354 | start = start + batchSize; 355 | } while ((start) < mintAddresses.length); 356 | } 357 | } 358 | 359 | export const closePool = async ( 360 | program: anchor.Program, 361 | authority: anchor.web3.Keypair, 362 | poolAccount: anchor.web3.PublicKey, // pool account public key 363 | configAccount: anchor.web3.PublicKey, // config account public key 364 | refundee: anchor.web3.PublicKey, // lamports refund to 365 | rewardRefundee: anchor.web3.PublicKey, // reward refund to 366 | rewardVault: anchor.web3.PublicKey, // reward vault 367 | ): Promise => { 368 | return await program.rpc.closePool( 369 | { 370 | accounts: { 371 | authority: authority.publicKey, 372 | poolAccount: poolAccount, 373 | config: configAccount, 374 | refundee: refundee, 375 | rewardRefundee: rewardRefundee, 376 | rewardVault: rewardVault, 377 | tokenProgram: TOKEN_PROGRAM_ID, 378 | }, 379 | signers: [authority], 380 | }); 381 | } 382 | 383 | (async () => { 384 | // const solConnection = new anchor.web3.Connection( 385 | // `http://127.0.0.1:8899`, 386 | // ); 387 | const solConnection = new anchor.web3.Connection( 388 | `https://api.${ENV}.solana.com/`, 389 | ); 390 | const walletKey = anchor.web3.Keypair.fromSecretKey( 391 | new Uint8Array(JSON.parse(readFileSync(keys_local).toString())), 392 | ); 393 | const walletWrapper = new anchor.Wallet(walletKey); 394 | const provider = new anchor.Provider(solConnection, walletWrapper, { 395 | preflightCommitment: 'recent', 396 | }); 397 | 398 | // const idl = await anchor.Program.fetchIdl(STAKE_PROGRAM, provider); 399 | const anchorProgram = new anchor.Program(idl, STAKE_PROGRAM, provider); 400 | 401 | console.log("Number of NFTs:", NUM_NFT) 402 | console.log("Reward Duration:", REWARD_DURATION) 403 | 404 | let [configAccount, configUuid] = await getConfigAccount(walletKey.publicKey); 405 | let [poolAccount, poolBump] = await getPoolAccount(walletKey.publicKey, configAccount); 406 | let [rewardAccount, rewardBump] = await getRewardAccount(walletKey.publicKey, poolAccount, WILD_TOKEN); 407 | 408 | 409 | // initialize pool - only run once 410 | let res = await initializePool( 411 | anchorProgram, 412 | walletKey, 413 | WILD_TOKEN, 414 | NUM_NFT, 415 | REWARD_DURATION, 416 | solConnection, 417 | configAccount, 418 | configUuid, 419 | poolAccount, 420 | poolBump, 421 | rewardAccount, 422 | rewardBump 423 | ) 424 | console.log(res) 425 | 426 | // get pool state 427 | let poolState = await getPoolState(anchorProgram, poolAccount) 428 | console.assert(poolState.isInitialized == true) 429 | console.assert(poolState.paused == true) 430 | // console.log(poolState) 431 | 432 | let configState = await getConfigState(anchorProgram, configAccount) 433 | let storedNFTs = configState.mintAddresses.map(element => element.toBase58()) 434 | let nftToUpload = NFT_ADDRESSES.filter((element) => !storedNFTs.includes(element)) 435 | 436 | // console.log(configState) 437 | 438 | // upload mint addresses to config - only run once 439 | await addMintAddresses( 440 | anchorProgram, 441 | walletKey, 442 | poolAccount, //POOL_ID, 443 | configAccount, //CONFIG_ID, 444 | nftToUpload 445 | ) 446 | 447 | // start the pool after upload config - only run once 448 | await resumePool(anchorProgram, walletKey, poolAccount)//POOL_ID) 449 | 450 | // get config state 451 | 452 | // console.assert(configState.numMint == configState.mintAddresses.length) 453 | // console.log('Number of NFTs:', configState.numMint) 454 | // console.log(configState.mintAddresses[0].toBase58()) 455 | 456 | //fund pool, authority == funder here - only run once 457 | await fund( 458 | anchorProgram, 459 | walletKey, 460 | poolAccount, 461 | configAccount, 462 | poolState.rewardVault, 463 | WILD_VAULT, 464 | walletKey.publicKey, 465 | 5_000_000_000_000_000 466 | ); 467 | console.log("Config Account:", configAccount.toBase58()) 468 | console.log("Pool Account:", poolAccount.toBase58()) 469 | console.log("rewardAccount:", rewardAccount.toBase58()) 470 | // // get reward vault info 471 | // let wildMint = await getMintInfo(provider, WILD_TOKEN); 472 | let rewardVaultInfo = await getTokenAccount(provider, poolState.rewardVault); 473 | // console.log(wildMint) 474 | console.log("reward vault amount:", rewardVaultInfo.amount.toNumber()) 475 | 476 | })(); 477 | 478 | 479 | 480 | -------------------------------------------------------------------------------- /client/nft_staking.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.0", 3 | "name": "nft_staking", 4 | "instructions": [ 5 | { 6 | "name": "initializePool", 7 | "accounts": [ 8 | { 9 | "name": "authority", 10 | "isMut": true, 11 | "isSigner": true 12 | }, 13 | { 14 | "name": "poolAccount", 15 | "isMut": true, 16 | "isSigner": false 17 | }, 18 | { 19 | "name": "config", 20 | "isMut": true, 21 | "isSigner": false 22 | }, 23 | { 24 | "name": "rewardMint", 25 | "isMut": false, 26 | "isSigner": false 27 | }, 28 | { 29 | "name": "rewardVault", 30 | "isMut": true, 31 | "isSigner": false 32 | }, 33 | { 34 | "name": "rent", 35 | "isMut": false, 36 | "isSigner": false 37 | }, 38 | { 39 | "name": "tokenProgram", 40 | "isMut": false, 41 | "isSigner": false 42 | }, 43 | { 44 | "name": "systemProgram", 45 | "isMut": false, 46 | "isSigner": false 47 | } 48 | ], 49 | "args": [ 50 | { 51 | "name": "poolBump", 52 | "type": "u8" 53 | }, 54 | { 55 | "name": "uuid", 56 | "type": "string" 57 | }, 58 | { 59 | "name": "numMint", 60 | "type": "u32" 61 | }, 62 | { 63 | "name": "rewardBump", 64 | "type": "u8" 65 | }, 66 | { 67 | "name": "rewardDuration", 68 | "type": "u64" 69 | } 70 | ] 71 | }, 72 | { 73 | "name": "addMintAddresses", 74 | "accounts": [ 75 | { 76 | "name": "authority", 77 | "isMut": true, 78 | "isSigner": true 79 | }, 80 | { 81 | "name": "poolAccount", 82 | "isMut": true, 83 | "isSigner": false 84 | }, 85 | { 86 | "name": "config", 87 | "isMut": true, 88 | "isSigner": false 89 | } 90 | ], 91 | "args": [ 92 | { 93 | "name": "mintAddresses", 94 | "type": { 95 | "vec": "publicKey" 96 | } 97 | }, 98 | { 99 | "name": "index", 100 | "type": "u32" 101 | } 102 | ] 103 | }, 104 | { 105 | "name": "pause", 106 | "accounts": [ 107 | { 108 | "name": "authority", 109 | "isMut": true, 110 | "isSigner": true 111 | }, 112 | { 113 | "name": "poolAccount", 114 | "isMut": true, 115 | "isSigner": false 116 | } 117 | ], 118 | "args": [] 119 | }, 120 | { 121 | "name": "resume", 122 | "accounts": [ 123 | { 124 | "name": "authority", 125 | "isMut": true, 126 | "isSigner": true 127 | }, 128 | { 129 | "name": "poolAccount", 130 | "isMut": true, 131 | "isSigner": false 132 | } 133 | ], 134 | "args": [] 135 | }, 136 | { 137 | "name": "authorizeFunder", 138 | "accounts": [ 139 | { 140 | "name": "authority", 141 | "isMut": true, 142 | "isSigner": true 143 | }, 144 | { 145 | "name": "poolAccount", 146 | "isMut": true, 147 | "isSigner": false 148 | } 149 | ], 150 | "args": [ 151 | { 152 | "name": "funderToAdd", 153 | "type": "publicKey" 154 | } 155 | ] 156 | }, 157 | { 158 | "name": "deauthorizeFunder", 159 | "accounts": [ 160 | { 161 | "name": "authority", 162 | "isMut": true, 163 | "isSigner": true 164 | }, 165 | { 166 | "name": "poolAccount", 167 | "isMut": true, 168 | "isSigner": false 169 | } 170 | ], 171 | "args": [ 172 | { 173 | "name": "funderToRemove", 174 | "type": "publicKey" 175 | } 176 | ] 177 | }, 178 | { 179 | "name": "fund", 180 | "accounts": [ 181 | { 182 | "name": "funder", 183 | "isMut": true, 184 | "isSigner": true 185 | }, 186 | { 187 | "name": "authority", 188 | "isMut": false, 189 | "isSigner": false 190 | }, 191 | { 192 | "name": "poolAccount", 193 | "isMut": true, 194 | "isSigner": false 195 | }, 196 | { 197 | "name": "config", 198 | "isMut": false, 199 | "isSigner": false 200 | }, 201 | { 202 | "name": "rewardVault", 203 | "isMut": true, 204 | "isSigner": false 205 | }, 206 | { 207 | "name": "funderVault", 208 | "isMut": true, 209 | "isSigner": false 210 | }, 211 | { 212 | "name": "tokenProgram", 213 | "isMut": false, 214 | "isSigner": false 215 | } 216 | ], 217 | "args": [ 218 | { 219 | "name": "amount", 220 | "type": "u64" 221 | } 222 | ] 223 | }, 224 | { 225 | "name": "createUser", 226 | "accounts": [ 227 | { 228 | "name": "user", 229 | "isMut": true, 230 | "isSigner": true 231 | }, 232 | { 233 | "name": "poolAccount", 234 | "isMut": true, 235 | "isSigner": false 236 | }, 237 | { 238 | "name": "userAccount", 239 | "isMut": true, 240 | "isSigner": false 241 | }, 242 | { 243 | "name": "mintStaked", 244 | "isMut": true, 245 | "isSigner": false 246 | }, 247 | { 248 | "name": "rent", 249 | "isMut": false, 250 | "isSigner": false 251 | }, 252 | { 253 | "name": "systemProgram", 254 | "isMut": false, 255 | "isSigner": false 256 | } 257 | ], 258 | "args": [ 259 | { 260 | "name": "userBump", 261 | "type": "u8" 262 | }, 263 | { 264 | "name": "mintStakedBump", 265 | "type": "u8" 266 | }, 267 | { 268 | "name": "uuid", 269 | "type": "string" 270 | } 271 | ] 272 | }, 273 | { 274 | "name": "stake", 275 | "accounts": [ 276 | { 277 | "name": "staker", 278 | "isMut": true, 279 | "isSigner": true 280 | }, 281 | { 282 | "name": "poolAccount", 283 | "isMut": true, 284 | "isSigner": false 285 | }, 286 | { 287 | "name": "config", 288 | "isMut": true, 289 | "isSigner": false 290 | }, 291 | { 292 | "name": "authority", 293 | "isMut": false, 294 | "isSigner": false 295 | }, 296 | { 297 | "name": "userAccount", 298 | "isMut": true, 299 | "isSigner": false 300 | }, 301 | { 302 | "name": "stakeFromAccount", 303 | "isMut": true, 304 | "isSigner": false 305 | }, 306 | { 307 | "name": "mintStaked", 308 | "isMut": true, 309 | "isSigner": false 310 | }, 311 | { 312 | "name": "currentMintStaked", 313 | "isMut": true, 314 | "isSigner": false 315 | }, 316 | { 317 | "name": "rent", 318 | "isMut": false, 319 | "isSigner": false 320 | }, 321 | { 322 | "name": "tokenProgram", 323 | "isMut": false, 324 | "isSigner": false 325 | }, 326 | { 327 | "name": "systemProgram", 328 | "isMut": false, 329 | "isSigner": false 330 | } 331 | ], 332 | "args": [ 333 | { 334 | "name": "mintStakedBump", 335 | "type": "u8" 336 | }, 337 | { 338 | "name": "uuid", 339 | "type": "string" 340 | } 341 | ] 342 | }, 343 | { 344 | "name": "unstake", 345 | "accounts": [ 346 | { 347 | "name": "staker", 348 | "isMut": true, 349 | "isSigner": true 350 | }, 351 | { 352 | "name": "poolAccount", 353 | "isMut": true, 354 | "isSigner": false 355 | }, 356 | { 357 | "name": "config", 358 | "isMut": true, 359 | "isSigner": false 360 | }, 361 | { 362 | "name": "authority", 363 | "isMut": false, 364 | "isSigner": false 365 | }, 366 | { 367 | "name": "userAccount", 368 | "isMut": true, 369 | "isSigner": false 370 | }, 371 | { 372 | "name": "unstakeFromAccount", 373 | "isMut": true, 374 | "isSigner": false 375 | }, 376 | { 377 | "name": "mintStaked", 378 | "isMut": true, 379 | "isSigner": false 380 | }, 381 | { 382 | "name": "currentMintStaked", 383 | "isMut": true, 384 | "isSigner": false 385 | }, 386 | { 387 | "name": "rent", 388 | "isMut": false, 389 | "isSigner": false 390 | }, 391 | { 392 | "name": "tokenProgram", 393 | "isMut": false, 394 | "isSigner": false 395 | }, 396 | { 397 | "name": "systemProgram", 398 | "isMut": false, 399 | "isSigner": false 400 | } 401 | ], 402 | "args": [ 403 | { 404 | "name": "mintStakedBump", 405 | "type": "u8" 406 | }, 407 | { 408 | "name": "uuid", 409 | "type": "string" 410 | } 411 | ] 412 | }, 413 | { 414 | "name": "claim", 415 | "accounts": [ 416 | { 417 | "name": "user", 418 | "isMut": true, 419 | "isSigner": true 420 | }, 421 | { 422 | "name": "poolAccount", 423 | "isMut": true, 424 | "isSigner": false 425 | }, 426 | { 427 | "name": "authority", 428 | "isMut": false, 429 | "isSigner": false 430 | }, 431 | { 432 | "name": "rewardVault", 433 | "isMut": true, 434 | "isSigner": false 435 | }, 436 | { 437 | "name": "userAccount", 438 | "isMut": true, 439 | "isSigner": false 440 | }, 441 | { 442 | "name": "rewardToAccount", 443 | "isMut": true, 444 | "isSigner": false 445 | }, 446 | { 447 | "name": "tokenProgram", 448 | "isMut": false, 449 | "isSigner": false 450 | } 451 | ], 452 | "args": [] 453 | }, 454 | { 455 | "name": "closeUser", 456 | "accounts": [ 457 | { 458 | "name": "user", 459 | "isMut": true, 460 | "isSigner": true 461 | }, 462 | { 463 | "name": "poolAccount", 464 | "isMut": true, 465 | "isSigner": false 466 | }, 467 | { 468 | "name": "userAccount", 469 | "isMut": true, 470 | "isSigner": false 471 | } 472 | ], 473 | "args": [] 474 | }, 475 | { 476 | "name": "closePool", 477 | "accounts": [ 478 | { 479 | "name": "authority", 480 | "isMut": true, 481 | "isSigner": true 482 | }, 483 | { 484 | "name": "poolAccount", 485 | "isMut": true, 486 | "isSigner": false 487 | }, 488 | { 489 | "name": "config", 490 | "isMut": true, 491 | "isSigner": false 492 | }, 493 | { 494 | "name": "refundee", 495 | "isMut": true, 496 | "isSigner": false 497 | }, 498 | { 499 | "name": "rewardRefundee", 500 | "isMut": true, 501 | "isSigner": false 502 | }, 503 | { 504 | "name": "rewardVault", 505 | "isMut": true, 506 | "isSigner": false 507 | }, 508 | { 509 | "name": "tokenProgram", 510 | "isMut": false, 511 | "isSigner": false 512 | } 513 | ], 514 | "args": [] 515 | } 516 | ], 517 | "accounts": [ 518 | { 519 | "name": "Pool", 520 | "type": { 521 | "kind": "struct", 522 | "fields": [ 523 | { 524 | "name": "isInitialized", 525 | "type": "bool" 526 | }, 527 | { 528 | "name": "authority", 529 | "type": "publicKey" 530 | }, 531 | { 532 | "name": "paused", 533 | "type": "bool" 534 | }, 535 | { 536 | "name": "config", 537 | "type": "publicKey" 538 | }, 539 | { 540 | "name": "rewardMint", 541 | "type": "publicKey" 542 | }, 543 | { 544 | "name": "rewardVault", 545 | "type": "publicKey" 546 | }, 547 | { 548 | "name": "lastUpdateTime", 549 | "type": "u64" 550 | }, 551 | { 552 | "name": "rewardRatePerToken", 553 | "type": "u128" 554 | }, 555 | { 556 | "name": "rewardDuration", 557 | "type": "u64" 558 | }, 559 | { 560 | "name": "rewardDurationEnd", 561 | "type": "u64" 562 | }, 563 | { 564 | "name": "tokenStakeCount", 565 | "type": "u32" 566 | }, 567 | { 568 | "name": "userCount", 569 | "type": "u32" 570 | }, 571 | { 572 | "name": "funders", 573 | "type": { 574 | "array": [ 575 | "publicKey", 576 | 5 577 | ] 578 | } 579 | } 580 | ] 581 | } 582 | }, 583 | { 584 | "name": "Config", 585 | "type": { 586 | "kind": "struct", 587 | "fields": [ 588 | { 589 | "name": "authority", 590 | "type": "publicKey" 591 | }, 592 | { 593 | "name": "uuid", 594 | "type": "string" 595 | }, 596 | { 597 | "name": "numMint", 598 | "type": "u32" 599 | }, 600 | { 601 | "name": "mintAddresses", 602 | "type": { 603 | "vec": "publicKey" 604 | } 605 | } 606 | ] 607 | } 608 | }, 609 | { 610 | "name": "User", 611 | "type": { 612 | "kind": "struct", 613 | "fields": [ 614 | { 615 | "name": "pool", 616 | "type": "publicKey" 617 | }, 618 | { 619 | "name": "user", 620 | "type": "publicKey" 621 | }, 622 | { 623 | "name": "rewardEarnedClaimed", 624 | "type": "u64" 625 | }, 626 | { 627 | "name": "rewardEarnedPending", 628 | "type": "u64" 629 | }, 630 | { 631 | "name": "mintStakedCount", 632 | "type": "u32" 633 | }, 634 | { 635 | "name": "uuid", 636 | "type": "string" 637 | }, 638 | { 639 | "name": "mintStaked", 640 | "type": "publicKey" 641 | }, 642 | { 643 | "name": "lastUpdateTime", 644 | "type": "u64" 645 | } 646 | ] 647 | } 648 | }, 649 | { 650 | "name": "MintStaked", 651 | "type": { 652 | "kind": "struct", 653 | "fields": [ 654 | { 655 | "name": "pool", 656 | "type": "publicKey" 657 | }, 658 | { 659 | "name": "userAccount", 660 | "type": "publicKey" 661 | }, 662 | { 663 | "name": "mintAccounts", 664 | "type": { 665 | "vec": "publicKey" 666 | } 667 | } 668 | ] 669 | } 670 | } 671 | ], 672 | "errors": [ 673 | { 674 | "code": 300, 675 | "name": "InsufficientTokenStake", 676 | "msg": "Insufficient tokens to stake." 677 | }, 678 | { 679 | "code": 301, 680 | "name": "InsufficientFundStake", 681 | "msg": "Insufficient funds to stake." 682 | }, 683 | { 684 | "code": 302, 685 | "name": "InsufficientFundUnstake", 686 | "msg": "Insufficient funds to unstake." 687 | }, 688 | { 689 | "code": 303, 690 | "name": "AmountMustBeGreaterThanZero", 691 | "msg": "Amount must be greater than zero." 692 | }, 693 | { 694 | "code": 304, 695 | "name": "SingleStakeTokenBCannotBeFunded", 696 | "msg": "Reward B cannot be funded - pool is single stake." 697 | }, 698 | { 699 | "code": 305, 700 | "name": "PoolPaused", 701 | "msg": "Pool is paused or is not initialized." 702 | }, 703 | { 704 | "code": 306, 705 | "name": "StakedMint", 706 | "msg": "User has staked mint" 707 | }, 708 | { 709 | "code": 307, 710 | "name": "PendingRewards", 711 | "msg": "User has pending rewards." 712 | }, 713 | { 714 | "code": 308, 715 | "name": "DurationTooShort", 716 | "msg": "Duration cannot be shorter than one day." 717 | }, 718 | { 719 | "code": 309, 720 | "name": "FunderAlreadyAuthorized", 721 | "msg": "Provided funder is already authorized to fund." 722 | }, 723 | { 724 | "code": 310, 725 | "name": "MaxFunders", 726 | "msg": "Maximum funders already authorized." 727 | }, 728 | { 729 | "code": 311, 730 | "name": "CannotDeauthorizePoolAuthority", 731 | "msg": "Cannot deauthorize the primary pool authority." 732 | }, 733 | { 734 | "code": 312, 735 | "name": "CannotDeauthorizeMissingAuthority", 736 | "msg": "Authority not found for deauthorization." 737 | }, 738 | { 739 | "code": 313, 740 | "name": "IndexGreaterThanLength", 741 | "msg": "Index greater than length!" 742 | }, 743 | { 744 | "code": 314, 745 | "name": "NumericalOverflowError", 746 | "msg": "Numerical overflow error!" 747 | } 748 | ] 749 | } -------------------------------------------------------------------------------- /client/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This script is for user activities 3 | Please refer to pool.ts for pool authority activities 4 | */ 5 | 6 | import * as anchor from "@project-serum/anchor"; 7 | import {readFileSync} from "fs"; 8 | import {PublicKey} from "@solana/web3.js"; 9 | import {AccountInfo, Token, TOKEN_PROGRAM_ID} from "@solana/spl-token"; 10 | import {getTokenAccount, parseTokenAccount} from "@project-serum/common"; 11 | 12 | export const STAKE_PROGRAM = new anchor.web3.PublicKey( 13 | "paramKFFuRPLVXZWjDRbnk5xKemduYZUW2BqUp7xZD3" 14 | ); 15 | 16 | //reward token mint 17 | export const WILD_TOKEN = new anchor.web3.PublicKey( 18 | "FpXFK3tZAWNb9eQMHNFpnWH1gQ1ssmSoz7av2VYcHscw" 19 | ) 20 | //reward token account 21 | export const WILD_VAULT = new anchor.web3.PublicKey( 22 | "ABGFm7tjupN9B2LSxZLvRHLWRkjdpqBF1kzfC7qQ27wZ" 23 | ) 24 | 25 | export const POOL_AUTHORITY = new anchor.web3.PublicKey( 26 | "" 27 | ) 28 | 29 | export const POOL_ID = new anchor.web3.PublicKey( 30 | "" 31 | ) 32 | 33 | export const CONFIG_ID = new anchor.web3.PublicKey( 34 | "" 35 | ) 36 | 37 | const idl = JSON.parse(require('fs').readFileSync('./client/nft_staking.json', 'utf8')); 38 | 39 | const keys_local = "" 40 | 41 | const ENV = "devnet"; 42 | 43 | 44 | const PREFIX = "nft_staking"; 45 | const PREFIX_CONFIG = "nft_staking_config"; 46 | const PREFIX_USER = "nft_staking_user" 47 | const PREFIX_MINT = "nft_staking_mint" 48 | 49 | export interface Pool { 50 | id: anchor.web3.PublicKey, 51 | connection: anchor.web3.Connection; 52 | program: anchor.Program; 53 | } 54 | 55 | interface PoolState { 56 | isInitialized: boolean; 57 | authority: anchor.web3.PublicKey; 58 | paused: boolean; 59 | config: anchor.web3.PublicKey; 60 | rewardMint: anchor.web3.PublicKey; 61 | rewardVault: anchor.web3.PublicKey; 62 | // rewardRatePerToken: u128 // how to conver it to number 63 | lastUpdateTime: number, 64 | rewardDuration: number, 65 | rewardDurationEnd: number, 66 | tokenStakeCount: number, 67 | userCount: number 68 | } 69 | 70 | interface ConfigState { 71 | authority: anchor.web3.PublicKey; 72 | uuid: string; 73 | numMint: number, 74 | mintAddresses: anchor.web3.PublicKey[]; 75 | } 76 | 77 | interface UserState { 78 | user: anchor.web3.PublicKey; 79 | rewardEarnedClaimed: number; 80 | rewardEarnedPending: number; 81 | mintStakedCount: number; 82 | uuid: string; 83 | mintStaked: anchor.web3.PublicKey; 84 | lastUpdateTime: number; 85 | mintStakedState: MintStakedState 86 | } 87 | 88 | interface MintStakedState { 89 | userAccount: anchor.web3.PublicKey; // user account address 90 | mintAccounts: anchor.web3.PublicKey[]; 91 | } 92 | 93 | export interface TokenInfo { 94 | account: anchor.web3.PublicKey; 95 | mint: anchor.web3.PublicKey, 96 | } 97 | 98 | export const getPoolState = async ( 99 | program: anchor.Program, 100 | poolId: anchor.web3.PublicKey, // pool account public key 101 | ): Promise => { 102 | let state = await program.account.pool.fetch(poolId); 103 | 104 | if (state == null) { 105 | return null; 106 | } 107 | let isInitialized = state.isInitialized; 108 | let authority = state.authority; 109 | let paused = state.paused; 110 | let config = state.config; 111 | let rewardMint = state.rewardMint 112 | let rewardVault = state.rewardVault 113 | let lastUpdateTime = state.lastUpdateTime.toNumber() 114 | let rewardDuration = state.rewardDuration.toNumber(); 115 | let rewardDurationEnd = state.rewardDurationEnd.toNumber(); 116 | let tokenStakeCount = state.tokenStakeCount; 117 | let userCount = state.userCount; 118 | return { 119 | isInitialized, 120 | authority, 121 | paused, 122 | config, 123 | rewardMint, 124 | rewardVault, 125 | lastUpdateTime, 126 | rewardDuration, 127 | rewardDurationEnd, 128 | tokenStakeCount, 129 | userCount 130 | } 131 | } 132 | 133 | export const getConfigState = async ( 134 | program: anchor.Program, 135 | configId: anchor.web3.PublicKey, // pool account public key 136 | ): Promise => { 137 | let state = await program.account.config.fetch(configId); 138 | 139 | if (state == null) { 140 | return null; 141 | } 142 | let authority = state.authority; 143 | let uuid = state.uuid; 144 | let numMint = state.numMint; 145 | let mintAddresses = state.mintAddresses.map((element) => element) 146 | return { 147 | authority, 148 | uuid, 149 | numMint, 150 | mintAddresses 151 | } 152 | } 153 | 154 | export const getUserState = async ( 155 | program: anchor.Program, 156 | userAccountId: anchor.web3.PublicKey, // pool account public key 157 | ): Promise => { 158 | let state = await program.account.user.fetch(userAccountId); 159 | 160 | if (state == null) { 161 | return null; 162 | } 163 | let mintStakedRes = await program.account.mintStaked.fetch(state.mintStaked); 164 | if (mintStakedRes == null) { 165 | return null; 166 | } 167 | 168 | let user = state.user; 169 | let rewardEarnedClaimed = state.rewardEarnedClaimed.toNumber(); 170 | let rewardEarnedPending = state.rewardEarnedPending.toNumber(); 171 | let mintStakedCount = state.mintStakedCount; 172 | let uuid = state.uuid; 173 | let mintStaked = state.mintStaked; 174 | let lastUpdateTime = state.lastUpdateTime.toNumber(); 175 | 176 | let userAccount = mintStakedRes.userAccount 177 | let mintAccounts = mintStakedRes.mintAccounts 178 | 179 | let mintStakedState: MintStakedState = { 180 | userAccount: userAccount, 181 | mintAccounts: [] 182 | } 183 | mintAccounts.forEach((e) => mintStakedState.mintAccounts.push(e)); 184 | 185 | return { 186 | user, 187 | rewardEarnedClaimed, 188 | rewardEarnedPending, 189 | mintStakedCount, 190 | uuid, 191 | mintStaked, 192 | lastUpdateTime, 193 | mintStakedState 194 | } 195 | 196 | } 197 | 198 | const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: PublicKey = new PublicKey( 199 | 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL', 200 | ); 201 | 202 | async function findAssociatedTokenAddress( 203 | walletAddress: PublicKey, 204 | tokenMintAddress: PublicKey 205 | ): Promise { 206 | return (await PublicKey.findProgramAddress( 207 | [ 208 | walletAddress.toBuffer(), 209 | TOKEN_PROGRAM_ID.toBuffer(), 210 | tokenMintAddress.toBuffer(), 211 | ], 212 | SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID 213 | ))[0]; 214 | } 215 | 216 | async function getOrCreateAssociatedTokenAccountInfo( 217 | provider: anchor.Provider, 218 | connection: anchor.web3.Connection, 219 | wallet: anchor.web3.Keypair, 220 | tokenMintAddress: PublicKey 221 | ): Promise { 222 | let rewardMint = new Token(connection, 223 | tokenMintAddress, 224 | TOKEN_PROGRAM_ID, 225 | wallet); 226 | return await rewardMint.getOrCreateAssociatedAccountInfo( 227 | wallet.publicKey 228 | ) 229 | } 230 | 231 | 232 | const generateUuid = (): string => { 233 | return anchor.web3.Keypair.generate() 234 | .publicKey.toBase58() 235 | .slice(0, 6); 236 | } 237 | 238 | const getConfigAccount = async ( 239 | authorityId: anchor.web3.PublicKey, 240 | configUuid: string, 241 | ): Promise<[anchor.web3.PublicKey, number]> => { 242 | return ( 243 | await anchor.web3.PublicKey.findProgramAddress( 244 | [ 245 | Buffer.from(anchor.utils.bytes.utf8.encode(PREFIX_CONFIG)), 246 | authorityId.toBuffer(), 247 | Buffer.from(configUuid) 248 | ], 249 | STAKE_PROGRAM 250 | ) 251 | ); 252 | }; 253 | 254 | const getPoolAccount = async ( 255 | authorityId: anchor.web3.PublicKey, 256 | configPubKey: anchor.web3.PublicKey, 257 | ): Promise<[anchor.web3.PublicKey, number]> => { 258 | return ( 259 | await anchor.web3.PublicKey.findProgramAddress( 260 | [ 261 | Buffer.from(anchor.utils.bytes.utf8.encode(PREFIX)), 262 | authorityId.toBuffer(), 263 | configPubKey.toBuffer() 264 | ], 265 | STAKE_PROGRAM 266 | ) 267 | ); 268 | }; 269 | 270 | const getRewardAccount = async ( 271 | authorityId: anchor.web3.PublicKey, 272 | poolId: anchor.web3.PublicKey, 273 | rewardMintId: anchor.web3.PublicKey, 274 | ): Promise<[anchor.web3.PublicKey, number]> => { 275 | return ( 276 | await anchor.web3.PublicKey.findProgramAddress( 277 | [Buffer.from(anchor.utils.bytes.utf8.encode(PREFIX)), 278 | poolId.toBuffer(), 279 | authorityId.toBuffer(), 280 | rewardMintId.toBuffer(),], 281 | STAKE_PROGRAM 282 | ) 283 | ); 284 | }; 285 | 286 | const getUserAccount = async ( 287 | poolAccount: anchor.web3.PublicKey, 288 | userWallet: anchor.web3.PublicKey, 289 | ): Promise<[anchor.web3.PublicKey, number]> => { 290 | return ( 291 | await anchor.web3.PublicKey.findProgramAddress( 292 | [Buffer.from(anchor.utils.bytes.utf8.encode(PREFIX_USER)), 293 | poolAccount.toBuffer(), 294 | userWallet.toBuffer(), 295 | ], 296 | STAKE_PROGRAM 297 | ) 298 | ); 299 | }; 300 | 301 | const getMintStakedAccount = async ( 302 | poolPda: anchor.web3.PublicKey, 303 | userAccount: anchor.web3.PublicKey, 304 | uuid: string 305 | ): Promise<[anchor.web3.PublicKey, number]> => { 306 | return ( 307 | await anchor.web3.PublicKey.findProgramAddress( 308 | [Buffer.from(anchor.utils.bytes.utf8.encode(PREFIX_MINT)), 309 | poolPda.toBuffer(), 310 | userAccount.toBuffer(), 311 | Buffer.from(uuid) 312 | ], 313 | STAKE_PROGRAM 314 | ) 315 | ); 316 | }; 317 | 318 | const getTokensByUser = async ( 319 | connection: anchor.web3.Connection, 320 | user: anchor.web3.PublicKey, 321 | ): Promise => { 322 | let tokenAccountsInfo = await connection.getTokenAccountsByOwner(user, {programId: TOKEN_PROGRAM_ID}); 323 | let tokenAccounts = [] 324 | 325 | if (tokenAccountsInfo.value.length > 0) { 326 | console.log('User has tokens in their wallet') 327 | 328 | for (let i = 0; i < tokenAccountsInfo.value.length; i++) { 329 | let res = await parseTokenAccount(tokenAccountsInfo.value[i].account.data) 330 | if (res != null) { 331 | let mintAddress = res.mint 332 | tokenAccounts.push({ 333 | "account": tokenAccountsInfo.value[i].pubkey, 334 | "mint": mintAddress 335 | }) 336 | } 337 | } 338 | } 339 | return tokenAccounts 340 | }; 341 | 342 | export const createUser = async ( 343 | program: anchor.Program, 344 | userWallet: anchor.web3.Keypair, 345 | poolAccount: anchor.web3.PublicKey, // pool account public key 346 | ): Promise => { 347 | let [userPda, userBump] = await getUserAccount( 348 | poolAccount, 349 | userWallet.publicKey, 350 | ) 351 | let mintStakedUuid = generateUuid(); 352 | 353 | let [userMintStakedAccount, userMintStakedBump] = await getMintStakedAccount( 354 | poolAccount, 355 | userPda, 356 | mintStakedUuid 357 | ) 358 | 359 | return await program.rpc.createUser( 360 | userBump, 361 | userMintStakedBump, 362 | mintStakedUuid, 363 | { 364 | accounts: { 365 | user: userWallet.publicKey, 366 | poolAccount: poolAccount, 367 | userAccount: userPda, 368 | mintStaked: userMintStakedAccount, 369 | rent: anchor.web3.SYSVAR_RENT_PUBKEY, 370 | systemProgram: anchor.web3.SystemProgram.programId, 371 | }, 372 | signers: [userWallet], 373 | }); 374 | } 375 | 376 | export const closeUser = async ( 377 | program: anchor.Program, 378 | userWallet: anchor.web3.Keypair, 379 | poolAccount: anchor.web3.PublicKey, 380 | userAccount: anchor.web3.PublicKey 381 | ): Promise => { 382 | return await program.rpc.closeUser( 383 | { 384 | accounts: { 385 | user: userWallet.publicKey, 386 | poolAccount: poolAccount, 387 | userAccount: userAccount, 388 | }, 389 | signers: [userWallet], 390 | }); 391 | } 392 | 393 | export const stake = async ( 394 | program: anchor.Program, 395 | userWallet: anchor.web3.Keypair, 396 | poolAccount: anchor.web3.PublicKey, 397 | configAccount: anchor.web3.PublicKey, 398 | userAccount: anchor.web3.PublicKey, 399 | stakeFromAccount: anchor.web3.PublicKey, 400 | currentMintStakedAccount: anchor.web3.PublicKey 401 | ): Promise => { 402 | 403 | let mintStakedUuid = generateUuid(); 404 | 405 | let [userMintStakedAccount, userMintStakedBump] = await getMintStakedAccount( 406 | poolAccount, 407 | userAccount, 408 | mintStakedUuid 409 | ) 410 | 411 | return program.rpc.stake( 412 | userMintStakedBump, 413 | mintStakedUuid, 414 | { 415 | accounts: { 416 | staker: userWallet.publicKey, 417 | poolAccount: poolAccount, 418 | config: configAccount, 419 | authority: POOL_AUTHORITY, 420 | userAccount: userAccount, 421 | stakeFromAccount: stakeFromAccount, 422 | mintStaked: userMintStakedAccount, 423 | currentMintStaked: currentMintStakedAccount, 424 | rent: anchor.web3.SYSVAR_RENT_PUBKEY, 425 | tokenProgram: TOKEN_PROGRAM_ID, 426 | systemProgram: anchor.web3.SystemProgram.programId, 427 | }, 428 | signers: [userWallet], 429 | }); 430 | } 431 | 432 | export const unstake = async ( 433 | program: anchor.Program, 434 | userWallet: anchor.web3.Keypair, 435 | poolAccount: anchor.web3.PublicKey, 436 | configAccount: anchor.web3.PublicKey, 437 | userAccount: anchor.web3.PublicKey, 438 | unstakeFromAccount: anchor.web3.PublicKey, 439 | currentMintStakedAccount: anchor.web3.PublicKey 440 | ): Promise => { 441 | 442 | let mintStakedUuid = generateUuid(); 443 | 444 | let [userMintStakedAccount, userMintStakedBump] = await getMintStakedAccount( 445 | poolAccount, 446 | userAccount, 447 | mintStakedUuid 448 | ) 449 | 450 | return program.rpc.unstake( 451 | userMintStakedBump, 452 | mintStakedUuid, 453 | { 454 | accounts: { 455 | staker: userWallet.publicKey, 456 | poolAccount: poolAccount, 457 | config: configAccount, 458 | authority: POOL_AUTHORITY, 459 | userAccount: userAccount, 460 | unstakeFromAccount: unstakeFromAccount, 461 | mintStaked: userMintStakedAccount, 462 | currentMintStaked: currentMintStakedAccount, 463 | rent: anchor.web3.SYSVAR_RENT_PUBKEY, 464 | tokenProgram: TOKEN_PROGRAM_ID, 465 | systemProgram: anchor.web3.SystemProgram.programId, 466 | }, 467 | signers: [userWallet], 468 | }); 469 | } 470 | 471 | export const claim = async ( 472 | program: anchor.Program, 473 | userWallet: anchor.web3.Keypair, 474 | poolAccount: anchor.web3.PublicKey, 475 | rewardAccount: anchor.web3.PublicKey, 476 | userAccount: anchor.web3.PublicKey, 477 | rewardToAccount: anchor.web3.PublicKey, 478 | ): Promise => { 479 | 480 | return program.rpc.claim( 481 | { 482 | accounts: { 483 | user: userWallet.publicKey, 484 | poolAccount: poolAccount, 485 | authority: POOL_AUTHORITY, 486 | rewardVault: rewardAccount, 487 | userAccount: userAccount, 488 | rewardToAccount: rewardToAccount, 489 | tokenProgram: TOKEN_PROGRAM_ID, 490 | }, 491 | signers: [userWallet], 492 | }); 493 | } 494 | 495 | (async () => { 496 | const solConnection = new anchor.web3.Connection( 497 | `https://api.${ENV}.solana.com/`, 498 | ); 499 | const walletKey = anchor.web3.Keypair.fromSecretKey( 500 | new Uint8Array(JSON.parse(readFileSync(keys_local).toString())), 501 | ); 502 | const walletWrapper = new anchor.Wallet(walletKey); 503 | const provider = new anchor.Provider(solConnection, walletWrapper, { 504 | preflightCommitment: 'recent', 505 | }); 506 | console.log("idl") 507 | 508 | const anchorProgram = new anchor.Program(idl, STAKE_PROGRAM, provider); 509 | 510 | // get pool state 511 | console.log("pool") 512 | console.log(anchorProgram) 513 | let poolState = await getPoolState(anchorProgram, POOL_ID) 514 | console.assert(poolState.isInitialized == true) 515 | console.assert(poolState.paused == false) 516 | console.log(poolState) 517 | 518 | // get config state 519 | let configState = await getConfigState(anchorProgram, CONFIG_ID) 520 | console.assert(configState.numMint == configState.mintAddresses.length) 521 | console.log('Number of NFTs:', configState.numMint) 522 | //console.log(configState) 523 | 524 | // check how much reward in the pool 525 | let rewardVaultInfo = await getTokenAccount(provider, poolState.rewardVault); 526 | console.log('Reward amount: ', rewardVaultInfo.amount.toNumber()) 527 | 528 | /// user activities 529 | // check tokens held by user 530 | let tokenAccounts = await getTokensByUser(solConnection, walletKey.publicKey) 531 | console.log(tokenAccounts) 532 | console.log(tokenAccounts[0]["mint"].toBase58()) 533 | 534 | 535 | // check if token in config 536 | // if (tokenAccounts.length > 0) { 537 | // console.log(configState.mintAddresses.some(e => e.toBase58() === tokenAccounts[0].mint.toBase58())) 538 | // } 539 | 540 | // prepare relevant keys 541 | 542 | let [poolAccount, _poolBump] = await getPoolAccount(POOL_AUTHORITY, CONFIG_ID); 543 | let [rewardAccount, _rewardBump] = await getRewardAccount(POOL_AUTHORITY, poolAccount, WILD_TOKEN); 544 | 545 | // create a user account in pool -- only run once 546 | 547 | await createUser( 548 | anchorProgram, 549 | walletKey, 550 | poolAccount // poolPda, not pool id 551 | ) 552 | 553 | // get user account info 554 | let [userAccount, _] = await getUserAccount( 555 | poolAccount, 556 | walletKey.publicKey, 557 | ) 558 | console.log(userAccount) 559 | 560 | let userState = await getUserState( 561 | anchorProgram, 562 | userAccount 563 | ) 564 | console.log('User State before stake: ', userState) 565 | 566 | // user stake one -- only run once 567 | let stakeFromAccount = tokenAccounts[1].account 568 | await stake( 569 | anchorProgram, 570 | walletKey, 571 | poolAccount, 572 | CONFIG_ID, 573 | userAccount, 574 | stakeFromAccount, 575 | userState.mintStaked 576 | ) 577 | 578 | userState = await getUserState( 579 | anchorProgram, 580 | userAccount 581 | ) 582 | // console.log('User State after stake: ', userState) 583 | // console.log(userState.mintStakedState.mintAccounts[0]) 584 | 585 | // // update user tokens information 586 | // tokenAccounts = await getTokensByUser(solConnection, walletKey.publicKey) 587 | // console.log(tokenAccounts.length, ' tokens in user wallet: ', tokenAccounts) 588 | 589 | // await sleep(5000); 590 | // user unstake 591 | await unstake( 592 | anchorProgram, 593 | walletKey, 594 | poolAccount, 595 | CONFIG_ID, 596 | userAccount, 597 | userState.mintStakedState.mintAccounts[0], 598 | userState.mintStaked 599 | ) 600 | 601 | // get user state 602 | userState = await getUserState( 603 | anchorProgram, 604 | userAccount 605 | ) 606 | // console.log('User State after unstake: ', userState) 607 | 608 | // update user tokens information 609 | // tokenAccounts = await getTokensByUser(solConnection, walletKey.publicKey) 610 | // console.log(tokenAccounts.length, ' tokens in user wallet: ', tokenAccounts) 611 | 612 | // user claim 613 | // create or get an associate wallet of WILD for user 614 | let rewardToAccountInfo = await getOrCreateAssociatedTokenAccountInfo( 615 | provider, 616 | solConnection, 617 | walletKey, 618 | WILD_TOKEN 619 | ) 620 | 621 | // console.log(rewardToAccountInfo) 622 | // await sleep(1000) 623 | // //console.log('User pending reward:', (await getPendingRewardsFunction(anchorProgram, poolAccount, userAccount)).toNumber()); 624 | // await sleep(1000) 625 | // //console.log('User pending reward:', (await getPendingRewardsFunction(anchorProgram, poolAccount, userAccount)).toNumber()); 626 | // await sleep(1000) 627 | // let [userAccount2, _1] = await getUserAccount( 628 | // poolAccount, 629 | // new anchor.web3.PublicKey("") 630 | // ) 631 | // console.log('User pending reward:', (await getPendingRewardsFunction(anchorProgram, 632 | // poolAccount, userAccount2)).toNumber()); 633 | // await claim( 634 | // anchorProgram, 635 | // walletKey, 636 | // poolAccount, 637 | // rewardAccount, 638 | // userAccount, 639 | // rewardToAccount 640 | // ) 641 | 642 | // check account balance 643 | // rewardToAccountInfo = await getTokenAccount(provider, rewardToAccountInfo.address); 644 | // console.log('Reward amount in user wallet: ', rewardToAccountInfo.amount.toNumber()) 645 | 646 | })(); 647 | 648 | const sleep = (ms: number): Promise => { 649 | return new Promise((resolve) => setTimeout(resolve, ms)); 650 | } 651 | 652 | async function getPendingRewardsFunction(rewardsPoolAnchorProgram, poolPubkey, userAccountPubkey) { 653 | 654 | const U64_MAX = new anchor.BN("18446744073709551615", 10); 655 | let poolObject = await rewardsPoolAnchorProgram.account.pool.fetch(poolPubkey); 656 | let userObject = await rewardsPoolAnchorProgram.account.user.fetch(userAccountPubkey); 657 | let rewardRatePerToken = poolObject.rewardRatePerToken; 658 | let mintStakedCount = new anchor.BN(userObject.mintStakedCount); 659 | let rewardEarnedPending = userObject.rewardEarnedPending; 660 | let lastUpdate = userObject.lastUpdateTime; 661 | 662 | let rewardBalance = await rewardsPoolAnchorProgram.provider.connection.getTokenAccountBalance(poolObject.rewardVault); 663 | // rewardBalance = new anchor.BN(parseInt(rewardBalance.value.amount)); 664 | 665 | let elapsed = new anchor.BN(Math.max(Math.floor(Date.now() / 1000) - lastUpdate, 0)); 666 | console.log("get pending rewards") 667 | console.log(rewardRatePerToken.div(U64_MAX).toNumber()) 668 | console.log(U64_MAX) 669 | console.log(mintStakedCount.toNumber()) 670 | console.log(elapsed.toNumber()) 671 | console.log(rewardEarnedPending.toNumber()) 672 | 673 | return rewardRatePerToken.div(U64_MAX).mul(mintStakedCount).mul(elapsed).add(rewardEarnedPending) 674 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.4.7" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "0.7.18" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "alloc-traits" 22 | version = "0.1.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483" 25 | 26 | [[package]] 27 | name = "anchor-attribute-access-control" 28 | version = "0.17.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "7b8ab97bfde16e49bc399586a857e9bd56e7c867a66a89ca809134d53d999138" 31 | dependencies = [ 32 | "anchor-syn", 33 | "anyhow", 34 | "proc-macro2", 35 | "quote", 36 | "regex", 37 | "syn", 38 | ] 39 | 40 | [[package]] 41 | name = "anchor-attribute-account" 42 | version = "0.17.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "40d3c2f1ebf823c4a8f0e41c57125991713177d4f02957600f8c1da8bd87adfd" 45 | dependencies = [ 46 | "anchor-syn", 47 | "anyhow", 48 | "bs58 0.4.0", 49 | "proc-macro2", 50 | "quote", 51 | "rustversion", 52 | "syn", 53 | ] 54 | 55 | [[package]] 56 | name = "anchor-attribute-error" 57 | version = "0.17.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "f8b5b954878c4cb1ad373143b42765abaf789691e13dbd0a3a8707dbfd0612cd" 60 | dependencies = [ 61 | "anchor-syn", 62 | "proc-macro2", 63 | "quote", 64 | "syn", 65 | ] 66 | 67 | [[package]] 68 | name = "anchor-attribute-event" 69 | version = "0.17.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "418daba265c778d2386c27191b4ec927c24be270ed6a8667be81de9e541c7a3e" 72 | dependencies = [ 73 | "anchor-syn", 74 | "anyhow", 75 | "proc-macro2", 76 | "quote", 77 | "syn", 78 | ] 79 | 80 | [[package]] 81 | name = "anchor-attribute-interface" 82 | version = "0.17.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "fd2159348897db16999d76ff396ba8722fb101e0e0cc6845b3722eb7472bd0d0" 85 | dependencies = [ 86 | "anchor-syn", 87 | "anyhow", 88 | "heck", 89 | "proc-macro2", 90 | "quote", 91 | "syn", 92 | ] 93 | 94 | [[package]] 95 | name = "anchor-attribute-program" 96 | version = "0.17.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "e6695b491d73439ad9839565beb0749107f5acca6d96b4cbaaaef428ba7b6c11" 99 | dependencies = [ 100 | "anchor-syn", 101 | "anyhow", 102 | "proc-macro2", 103 | "quote", 104 | "syn", 105 | ] 106 | 107 | [[package]] 108 | name = "anchor-attribute-state" 109 | version = "0.17.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "4bcbbeade2b868e597b55d90418dc51334c4e388f988c0eea1af5d511083ed10" 112 | dependencies = [ 113 | "anchor-syn", 114 | "anyhow", 115 | "proc-macro2", 116 | "quote", 117 | "syn", 118 | ] 119 | 120 | [[package]] 121 | name = "anchor-derive-accounts" 122 | version = "0.17.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "dc82ef304c38e7529883176c428acfab9a7bb9e851aa694fff53c8789fbc47b3" 125 | dependencies = [ 126 | "anchor-syn", 127 | "anyhow", 128 | "proc-macro2", 129 | "quote", 130 | "syn", 131 | ] 132 | 133 | [[package]] 134 | name = "anchor-lang" 135 | version = "0.17.0" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "ff6b7025eb65638005fd2af58e2bd136b61c2ecbadda379e908a5af541351a3a" 138 | dependencies = [ 139 | "anchor-attribute-access-control", 140 | "anchor-attribute-account", 141 | "anchor-attribute-error", 142 | "anchor-attribute-event", 143 | "anchor-attribute-interface", 144 | "anchor-attribute-program", 145 | "anchor-attribute-state", 146 | "anchor-derive-accounts", 147 | "base64 0.13.0", 148 | "borsh", 149 | "bytemuck", 150 | "solana-program", 151 | "thiserror", 152 | ] 153 | 154 | [[package]] 155 | name = "anchor-spl" 156 | version = "0.17.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "b49dfaf04f0794ecbdafa1f5dda93d47fc042ae70478fc079194c6c7cd265e94" 159 | dependencies = [ 160 | "anchor-lang", 161 | "lazy_static", 162 | "serum_dex", 163 | "solana-program", 164 | "spl-associated-token-account", 165 | "spl-token", 166 | ] 167 | 168 | [[package]] 169 | name = "anchor-syn" 170 | version = "0.17.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "321cca8ea1c35b199956e11b2869e8b1b1ae2d547326a12fc45375d0806470c8" 173 | dependencies = [ 174 | "anyhow", 175 | "bs58 0.3.1", 176 | "heck", 177 | "proc-macro2", 178 | "proc-macro2-diagnostics", 179 | "quote", 180 | "serde", 181 | "serde_json", 182 | "sha2", 183 | "syn", 184 | "thiserror", 185 | ] 186 | 187 | [[package]] 188 | name = "anyhow" 189 | version = "1.0.51" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203" 192 | 193 | [[package]] 194 | name = "arrayref" 195 | version = "0.3.6" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 198 | 199 | [[package]] 200 | name = "arrayvec" 201 | version = "0.5.2" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 204 | 205 | [[package]] 206 | name = "atty" 207 | version = "0.2.14" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 210 | dependencies = [ 211 | "hermit-abi", 212 | "libc", 213 | "winapi", 214 | ] 215 | 216 | [[package]] 217 | name = "autocfg" 218 | version = "1.0.1" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 221 | 222 | [[package]] 223 | name = "base64" 224 | version = "0.12.3" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 227 | 228 | [[package]] 229 | name = "base64" 230 | version = "0.13.0" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 233 | 234 | [[package]] 235 | name = "bincode" 236 | version = "1.3.3" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 239 | dependencies = [ 240 | "serde", 241 | ] 242 | 243 | [[package]] 244 | name = "blake3" 245 | version = "0.3.8" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3" 248 | dependencies = [ 249 | "arrayref", 250 | "arrayvec", 251 | "cc", 252 | "cfg-if 0.1.10", 253 | "constant_time_eq", 254 | "crypto-mac", 255 | "digest 0.9.0", 256 | ] 257 | 258 | [[package]] 259 | name = "block-buffer" 260 | version = "0.9.0" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 263 | dependencies = [ 264 | "block-padding", 265 | "generic-array 0.14.4", 266 | ] 267 | 268 | [[package]] 269 | name = "block-padding" 270 | version = "0.2.1" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" 273 | 274 | [[package]] 275 | name = "borsh" 276 | version = "0.9.1" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "18dda7dc709193c0d86a1a51050a926dc3df1cf262ec46a23a25dba421ea1924" 279 | dependencies = [ 280 | "borsh-derive", 281 | "hashbrown", 282 | ] 283 | 284 | [[package]] 285 | name = "borsh-derive" 286 | version = "0.9.1" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "684155372435f578c0fa1acd13ebbb182cc19d6b38b64ae7901da4393217d264" 289 | dependencies = [ 290 | "borsh-derive-internal", 291 | "borsh-schema-derive-internal", 292 | "proc-macro-crate 0.1.5", 293 | "proc-macro2", 294 | "syn", 295 | ] 296 | 297 | [[package]] 298 | name = "borsh-derive-internal" 299 | version = "0.9.1" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "2102f62f8b6d3edeab871830782285b64cc1830168094db05c8e458f209bc5c3" 302 | dependencies = [ 303 | "proc-macro2", 304 | "quote", 305 | "syn", 306 | ] 307 | 308 | [[package]] 309 | name = "borsh-schema-derive-internal" 310 | version = "0.9.1" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "196c978c4c9b0b142d446ef3240690bf5a8a33497074a113ff9a337ccb750483" 313 | dependencies = [ 314 | "proc-macro2", 315 | "quote", 316 | "syn", 317 | ] 318 | 319 | [[package]] 320 | name = "bs58" 321 | version = "0.3.1" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" 324 | 325 | [[package]] 326 | name = "bs58" 327 | version = "0.4.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" 330 | 331 | [[package]] 332 | name = "bv" 333 | version = "0.11.1" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" 336 | dependencies = [ 337 | "feature-probe", 338 | "serde", 339 | ] 340 | 341 | [[package]] 342 | name = "bytemuck" 343 | version = "1.7.3" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" 346 | 347 | [[package]] 348 | name = "byteorder" 349 | version = "1.4.3" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 352 | 353 | [[package]] 354 | name = "cc" 355 | version = "1.0.72" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 358 | 359 | [[package]] 360 | name = "cfg-if" 361 | version = "0.1.10" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 364 | 365 | [[package]] 366 | name = "cfg-if" 367 | version = "1.0.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 370 | 371 | [[package]] 372 | name = "constant_time_eq" 373 | version = "0.1.5" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 376 | 377 | [[package]] 378 | name = "cpufeatures" 379 | version = "0.2.1" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" 382 | dependencies = [ 383 | "libc", 384 | ] 385 | 386 | [[package]] 387 | name = "crunchy" 388 | version = "0.2.2" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 391 | 392 | [[package]] 393 | name = "crypto-mac" 394 | version = "0.8.0" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" 397 | dependencies = [ 398 | "generic-array 0.14.4", 399 | "subtle", 400 | ] 401 | 402 | [[package]] 403 | name = "curve25519-dalek" 404 | version = "2.1.3" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" 407 | dependencies = [ 408 | "byteorder", 409 | "digest 0.8.1", 410 | "rand_core", 411 | "subtle", 412 | "zeroize", 413 | ] 414 | 415 | [[package]] 416 | name = "derivative" 417 | version = "2.2.0" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 420 | dependencies = [ 421 | "proc-macro2", 422 | "quote", 423 | "syn", 424 | ] 425 | 426 | [[package]] 427 | name = "digest" 428 | version = "0.8.1" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 431 | dependencies = [ 432 | "generic-array 0.12.4", 433 | ] 434 | 435 | [[package]] 436 | name = "digest" 437 | version = "0.9.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 440 | dependencies = [ 441 | "generic-array 0.14.4", 442 | ] 443 | 444 | [[package]] 445 | name = "either" 446 | version = "1.6.1" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 449 | 450 | [[package]] 451 | name = "enumflags2" 452 | version = "0.6.4" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "83c8d82922337cd23a15f88b70d8e4ef5f11da38dd7cdb55e84dd5de99695da0" 455 | dependencies = [ 456 | "enumflags2_derive", 457 | ] 458 | 459 | [[package]] 460 | name = "enumflags2_derive" 461 | version = "0.6.4" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" 464 | dependencies = [ 465 | "proc-macro2", 466 | "quote", 467 | "syn", 468 | ] 469 | 470 | [[package]] 471 | name = "env_logger" 472 | version = "0.8.4" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" 475 | dependencies = [ 476 | "atty", 477 | "humantime", 478 | "log", 479 | "regex", 480 | "termcolor", 481 | ] 482 | 483 | [[package]] 484 | name = "feature-probe" 485 | version = "0.1.1" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" 488 | 489 | [[package]] 490 | name = "field-offset" 491 | version = "0.3.4" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" 494 | dependencies = [ 495 | "memoffset", 496 | "rustc_version 0.3.3", 497 | ] 498 | 499 | [[package]] 500 | name = "generic-array" 501 | version = "0.12.4" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 504 | dependencies = [ 505 | "typenum", 506 | ] 507 | 508 | [[package]] 509 | name = "generic-array" 510 | version = "0.14.4" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 513 | dependencies = [ 514 | "serde", 515 | "typenum", 516 | "version_check", 517 | ] 518 | 519 | [[package]] 520 | name = "getrandom" 521 | version = "0.1.16" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 524 | dependencies = [ 525 | "cfg-if 1.0.0", 526 | "libc", 527 | "wasi", 528 | ] 529 | 530 | [[package]] 531 | name = "hashbrown" 532 | version = "0.9.1" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 535 | dependencies = [ 536 | "ahash", 537 | ] 538 | 539 | [[package]] 540 | name = "heck" 541 | version = "0.3.3" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 544 | dependencies = [ 545 | "unicode-segmentation", 546 | ] 547 | 548 | [[package]] 549 | name = "hermit-abi" 550 | version = "0.1.19" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 553 | dependencies = [ 554 | "libc", 555 | ] 556 | 557 | [[package]] 558 | name = "hex" 559 | version = "0.4.3" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 562 | 563 | [[package]] 564 | name = "hmac" 565 | version = "0.8.1" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" 568 | dependencies = [ 569 | "crypto-mac", 570 | "digest 0.9.0", 571 | ] 572 | 573 | [[package]] 574 | name = "hmac-drbg" 575 | version = "0.3.0" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" 578 | dependencies = [ 579 | "digest 0.9.0", 580 | "generic-array 0.14.4", 581 | "hmac", 582 | ] 583 | 584 | [[package]] 585 | name = "humantime" 586 | version = "2.1.0" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 589 | 590 | [[package]] 591 | name = "itertools" 592 | version = "0.9.0" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" 595 | dependencies = [ 596 | "either", 597 | ] 598 | 599 | [[package]] 600 | name = "itoa" 601 | version = "1.0.1" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 604 | 605 | [[package]] 606 | name = "keccak" 607 | version = "0.1.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" 610 | 611 | [[package]] 612 | name = "lazy_static" 613 | version = "1.4.0" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 616 | 617 | [[package]] 618 | name = "libc" 619 | version = "0.2.112" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" 622 | 623 | [[package]] 624 | name = "libsecp256k1" 625 | version = "0.5.0" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "bd1137239ab33b41aa9637a88a28249e5e70c40a42ccc92db7f12cc356c1fcd7" 628 | dependencies = [ 629 | "arrayref", 630 | "base64 0.12.3", 631 | "digest 0.9.0", 632 | "hmac-drbg", 633 | "libsecp256k1-core", 634 | "libsecp256k1-gen-ecmult", 635 | "libsecp256k1-gen-genmult", 636 | "rand", 637 | "serde", 638 | "sha2", 639 | "typenum", 640 | ] 641 | 642 | [[package]] 643 | name = "libsecp256k1-core" 644 | version = "0.2.2" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" 647 | dependencies = [ 648 | "crunchy", 649 | "digest 0.9.0", 650 | "subtle", 651 | ] 652 | 653 | [[package]] 654 | name = "libsecp256k1-gen-ecmult" 655 | version = "0.2.1" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" 658 | dependencies = [ 659 | "libsecp256k1-core", 660 | ] 661 | 662 | [[package]] 663 | name = "libsecp256k1-gen-genmult" 664 | version = "0.2.1" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" 667 | dependencies = [ 668 | "libsecp256k1-core", 669 | ] 670 | 671 | [[package]] 672 | name = "log" 673 | version = "0.4.14" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 676 | dependencies = [ 677 | "cfg-if 1.0.0", 678 | ] 679 | 680 | [[package]] 681 | name = "memchr" 682 | version = "2.4.1" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 685 | 686 | [[package]] 687 | name = "memmap2" 688 | version = "0.1.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" 691 | dependencies = [ 692 | "libc", 693 | ] 694 | 695 | [[package]] 696 | name = "memoffset" 697 | version = "0.6.5" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 700 | dependencies = [ 701 | "autocfg", 702 | ] 703 | 704 | [[package]] 705 | name = "nft_staking" 706 | version = "0.1.0" 707 | dependencies = [ 708 | "anchor-lang", 709 | "anchor-spl", 710 | "arrayref", 711 | "spl-token", 712 | ] 713 | 714 | [[package]] 715 | name = "num-derive" 716 | version = "0.3.3" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" 719 | dependencies = [ 720 | "proc-macro2", 721 | "quote", 722 | "syn", 723 | ] 724 | 725 | [[package]] 726 | name = "num-traits" 727 | version = "0.2.14" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 730 | dependencies = [ 731 | "autocfg", 732 | ] 733 | 734 | [[package]] 735 | name = "num_enum" 736 | version = "0.5.5" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "085fe377a4b2805c0fbc09484415ec261174614b7f080b0e0d520456ac421a67" 739 | dependencies = [ 740 | "derivative", 741 | "num_enum_derive", 742 | ] 743 | 744 | [[package]] 745 | name = "num_enum_derive" 746 | version = "0.5.5" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "5249369707a1e07b39f78d98c8f34e00aca7dcb053812fdbb5ad7be82c1bba38" 749 | dependencies = [ 750 | "proc-macro-crate 1.1.0", 751 | "proc-macro2", 752 | "quote", 753 | "syn", 754 | ] 755 | 756 | [[package]] 757 | name = "opaque-debug" 758 | version = "0.3.0" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 761 | 762 | [[package]] 763 | name = "pest" 764 | version = "2.1.3" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 767 | dependencies = [ 768 | "ucd-trie", 769 | ] 770 | 771 | [[package]] 772 | name = "ppv-lite86" 773 | version = "0.2.15" 774 | source = "registry+https://github.com/rust-lang/crates.io-index" 775 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" 776 | 777 | [[package]] 778 | name = "proc-macro-crate" 779 | version = "0.1.5" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" 782 | dependencies = [ 783 | "toml", 784 | ] 785 | 786 | [[package]] 787 | name = "proc-macro-crate" 788 | version = "1.1.0" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" 791 | dependencies = [ 792 | "thiserror", 793 | "toml", 794 | ] 795 | 796 | [[package]] 797 | name = "proc-macro2" 798 | version = "1.0.34" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" 801 | dependencies = [ 802 | "unicode-xid", 803 | ] 804 | 805 | [[package]] 806 | name = "proc-macro2-diagnostics" 807 | version = "0.9.1" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" 810 | dependencies = [ 811 | "proc-macro2", 812 | "quote", 813 | "syn", 814 | "version_check", 815 | "yansi", 816 | ] 817 | 818 | [[package]] 819 | name = "quote" 820 | version = "1.0.10" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 823 | dependencies = [ 824 | "proc-macro2", 825 | ] 826 | 827 | [[package]] 828 | name = "rand" 829 | version = "0.7.3" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 832 | dependencies = [ 833 | "getrandom", 834 | "libc", 835 | "rand_chacha", 836 | "rand_core", 837 | "rand_hc", 838 | ] 839 | 840 | [[package]] 841 | name = "rand_chacha" 842 | version = "0.2.2" 843 | source = "registry+https://github.com/rust-lang/crates.io-index" 844 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 845 | dependencies = [ 846 | "ppv-lite86", 847 | "rand_core", 848 | ] 849 | 850 | [[package]] 851 | name = "rand_core" 852 | version = "0.5.1" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 855 | dependencies = [ 856 | "getrandom", 857 | ] 858 | 859 | [[package]] 860 | name = "rand_hc" 861 | version = "0.2.0" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 864 | dependencies = [ 865 | "rand_core", 866 | ] 867 | 868 | [[package]] 869 | name = "regex" 870 | version = "1.5.4" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 873 | dependencies = [ 874 | "aho-corasick", 875 | "memchr", 876 | "regex-syntax", 877 | ] 878 | 879 | [[package]] 880 | name = "regex-syntax" 881 | version = "0.6.25" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 884 | 885 | [[package]] 886 | name = "rustc_version" 887 | version = "0.2.3" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 890 | dependencies = [ 891 | "semver 0.9.0", 892 | ] 893 | 894 | [[package]] 895 | name = "rustc_version" 896 | version = "0.3.3" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" 899 | dependencies = [ 900 | "semver 0.11.0", 901 | ] 902 | 903 | [[package]] 904 | name = "rustversion" 905 | version = "1.0.6" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" 908 | 909 | [[package]] 910 | name = "ryu" 911 | version = "1.0.9" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 914 | 915 | [[package]] 916 | name = "safe-transmute" 917 | version = "0.11.2" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "98a01dab6acf992653be49205bdd549f32f17cb2803e8eacf1560bf97259aae8" 920 | 921 | [[package]] 922 | name = "semver" 923 | version = "0.9.0" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 926 | dependencies = [ 927 | "semver-parser 0.7.0", 928 | ] 929 | 930 | [[package]] 931 | name = "semver" 932 | version = "0.11.0" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 935 | dependencies = [ 936 | "semver-parser 0.10.2", 937 | ] 938 | 939 | [[package]] 940 | name = "semver-parser" 941 | version = "0.7.0" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 944 | 945 | [[package]] 946 | name = "semver-parser" 947 | version = "0.10.2" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 950 | dependencies = [ 951 | "pest", 952 | ] 953 | 954 | [[package]] 955 | name = "serde" 956 | version = "1.0.132" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008" 959 | dependencies = [ 960 | "serde_derive", 961 | ] 962 | 963 | [[package]] 964 | name = "serde_bytes" 965 | version = "0.11.5" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" 968 | dependencies = [ 969 | "serde", 970 | ] 971 | 972 | [[package]] 973 | name = "serde_derive" 974 | version = "1.0.132" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276" 977 | dependencies = [ 978 | "proc-macro2", 979 | "quote", 980 | "syn", 981 | ] 982 | 983 | [[package]] 984 | name = "serde_json" 985 | version = "1.0.73" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" 988 | dependencies = [ 989 | "itoa", 990 | "ryu", 991 | "serde", 992 | ] 993 | 994 | [[package]] 995 | name = "serum_dex" 996 | version = "0.4.0" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "02705854bae4622e552346c8edd43ab90c7425da35d63d2c689f39238f8d8b25" 999 | dependencies = [ 1000 | "arrayref", 1001 | "bincode", 1002 | "bytemuck", 1003 | "byteorder", 1004 | "enumflags2", 1005 | "field-offset", 1006 | "itertools", 1007 | "num-traits", 1008 | "num_enum", 1009 | "safe-transmute", 1010 | "serde", 1011 | "solana-program", 1012 | "spl-token", 1013 | "static_assertions", 1014 | "thiserror", 1015 | "without-alloc", 1016 | ] 1017 | 1018 | [[package]] 1019 | name = "sha2" 1020 | version = "0.9.8" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" 1023 | dependencies = [ 1024 | "block-buffer", 1025 | "cfg-if 1.0.0", 1026 | "cpufeatures", 1027 | "digest 0.9.0", 1028 | "opaque-debug", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "sha3" 1033 | version = "0.9.1" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" 1036 | dependencies = [ 1037 | "block-buffer", 1038 | "digest 0.9.0", 1039 | "keccak", 1040 | "opaque-debug", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "solana-frozen-abi" 1045 | version = "1.7.11" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "21ddfc2b65a555c0e0156c043bce092d473bc4f00daa7ca3c223d97d92d2e807" 1048 | dependencies = [ 1049 | "bs58 0.3.1", 1050 | "bv", 1051 | "generic-array 0.14.4", 1052 | "log", 1053 | "memmap2", 1054 | "rustc_version 0.2.3", 1055 | "serde", 1056 | "serde_derive", 1057 | "sha2", 1058 | "solana-frozen-abi-macro", 1059 | "solana-logger", 1060 | "thiserror", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "solana-frozen-abi-macro" 1065 | version = "1.7.11" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "a876aa31298fdee6560c8ee0695ebed313bbdbb6fbbee439ac3b9df8aebfb87c" 1068 | dependencies = [ 1069 | "proc-macro2", 1070 | "quote", 1071 | "rustc_version 0.2.3", 1072 | "syn", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "solana-logger" 1077 | version = "1.7.11" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "98a07290cc521e529bff0b0afd3aacd1d3904a41f35321ede6d1f3574efa3e94" 1080 | dependencies = [ 1081 | "env_logger", 1082 | "lazy_static", 1083 | "log", 1084 | ] 1085 | 1086 | [[package]] 1087 | name = "solana-program" 1088 | version = "1.7.11" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "49ffc60d33a318300682e42d28ff4f1276327f6374cab9591c8620a54be7aec1" 1091 | dependencies = [ 1092 | "bincode", 1093 | "blake3", 1094 | "borsh", 1095 | "borsh-derive", 1096 | "bs58 0.3.1", 1097 | "bv", 1098 | "curve25519-dalek", 1099 | "hex", 1100 | "itertools", 1101 | "lazy_static", 1102 | "libsecp256k1", 1103 | "log", 1104 | "num-derive", 1105 | "num-traits", 1106 | "rand", 1107 | "rustc_version 0.2.3", 1108 | "rustversion", 1109 | "serde", 1110 | "serde_bytes", 1111 | "serde_derive", 1112 | "sha2", 1113 | "sha3", 1114 | "solana-frozen-abi", 1115 | "solana-frozen-abi-macro", 1116 | "solana-logger", 1117 | "solana-sdk-macro", 1118 | "thiserror", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "solana-sdk-macro" 1123 | version = "1.7.11" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "b453dca160617b1676c47e3cfd4361f455dc5bb1c93659ec84b0c5d566b5c039" 1126 | dependencies = [ 1127 | "bs58 0.3.1", 1128 | "proc-macro2", 1129 | "quote", 1130 | "rustversion", 1131 | "syn", 1132 | ] 1133 | 1134 | [[package]] 1135 | name = "spl-associated-token-account" 1136 | version = "1.0.3" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "393e2240d521c3dd770806bff25c2c00d761ac962be106e14e22dd912007f428" 1139 | dependencies = [ 1140 | "solana-program", 1141 | "spl-token", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "spl-token" 1146 | version = "3.2.0" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "93bfdd5bd7c869cb565c7d7635c4fafe189b988a0bdef81063cd9585c6b8dc01" 1149 | dependencies = [ 1150 | "arrayref", 1151 | "num-derive", 1152 | "num-traits", 1153 | "num_enum", 1154 | "solana-program", 1155 | "thiserror", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "static_assertions" 1160 | version = "1.1.0" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1163 | 1164 | [[package]] 1165 | name = "subtle" 1166 | version = "2.4.1" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 1169 | 1170 | [[package]] 1171 | name = "syn" 1172 | version = "1.0.82" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" 1175 | dependencies = [ 1176 | "proc-macro2", 1177 | "quote", 1178 | "unicode-xid", 1179 | ] 1180 | 1181 | [[package]] 1182 | name = "termcolor" 1183 | version = "1.1.2" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 1186 | dependencies = [ 1187 | "winapi-util", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "thiserror" 1192 | version = "1.0.30" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 1195 | dependencies = [ 1196 | "thiserror-impl", 1197 | ] 1198 | 1199 | [[package]] 1200 | name = "thiserror-impl" 1201 | version = "1.0.30" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 1204 | dependencies = [ 1205 | "proc-macro2", 1206 | "quote", 1207 | "syn", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "toml" 1212 | version = "0.5.8" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1215 | dependencies = [ 1216 | "serde", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "typenum" 1221 | version = "1.14.0" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" 1224 | 1225 | [[package]] 1226 | name = "ucd-trie" 1227 | version = "0.1.3" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 1230 | 1231 | [[package]] 1232 | name = "unicode-segmentation" 1233 | version = "1.8.0" 1234 | source = "registry+https://github.com/rust-lang/crates.io-index" 1235 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 1236 | 1237 | [[package]] 1238 | name = "unicode-xid" 1239 | version = "0.2.2" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1242 | 1243 | [[package]] 1244 | name = "version_check" 1245 | version = "0.9.3" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1248 | 1249 | [[package]] 1250 | name = "wasi" 1251 | version = "0.9.0+wasi-snapshot-preview1" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1254 | 1255 | [[package]] 1256 | name = "winapi" 1257 | version = "0.3.9" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1260 | dependencies = [ 1261 | "winapi-i686-pc-windows-gnu", 1262 | "winapi-x86_64-pc-windows-gnu", 1263 | ] 1264 | 1265 | [[package]] 1266 | name = "winapi-i686-pc-windows-gnu" 1267 | version = "0.4.0" 1268 | source = "registry+https://github.com/rust-lang/crates.io-index" 1269 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1270 | 1271 | [[package]] 1272 | name = "winapi-util" 1273 | version = "0.1.5" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1276 | dependencies = [ 1277 | "winapi", 1278 | ] 1279 | 1280 | [[package]] 1281 | name = "winapi-x86_64-pc-windows-gnu" 1282 | version = "0.4.0" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1285 | 1286 | [[package]] 1287 | name = "without-alloc" 1288 | version = "0.2.1" 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" 1290 | checksum = "5e34736feff52a0b3e5680927e947a4d8fac1f0b80dc8120b080dd8de24d75e2" 1291 | dependencies = [ 1292 | "alloc-traits", 1293 | ] 1294 | 1295 | [[package]] 1296 | name = "yansi" 1297 | version = "0.5.0" 1298 | source = "registry+https://github.com/rust-lang/crates.io-index" 1299 | checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" 1300 | 1301 | [[package]] 1302 | name = "zeroize" 1303 | version = "1.4.3" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" 1306 | -------------------------------------------------------------------------------- /programs/nft-staking/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod utils; 2 | 3 | use std::cell::Ref; 4 | use std::cmp; 5 | use crate::constants::*; 6 | use anchor_lang::prelude::*; 7 | use anchor_spl::token::{TokenAccount}; 8 | use anchor_lang::solana_program::{clock, log::sol_log}; 9 | use std::convert::Into; 10 | use std::convert::TryInto; 11 | use arrayref::array_ref; 12 | 13 | const PREFIX: &str = "nft_staking"; 14 | const PREFIX_USER: &str = "nft_staking_user"; 15 | const PREFIX_MINT: &str = "nft_staking_mint"; 16 | const PRECISION: u128 = u64::MAX as u128; 17 | 18 | declare_id!("paramKFFuRPLVXZWjDRbnk5xKemduYZUW2BqUp7xZD3"); 19 | 20 | pub mod constants { 21 | pub const MIN_DURATION: u64 = 1; 22 | 23 | pub const MAX_MINT_LIMIT: usize = 300000; 24 | 25 | pub const PUBKEY_SIZE: usize = 32; 26 | } 27 | 28 | pub fn get_config_count(data: &Ref<&mut [u8]>) -> core::result::Result { 29 | return Ok(u32::from_le_bytes(*array_ref![data, CONFIG_SIZE_START, 4]) as usize); 30 | } 31 | 32 | pub fn check_mint_address(data: &Ref<&mut [u8]>, mint_address: &[u8; 32]) -> core::result::Result { 33 | let mut position = CONFIG_SIZE_START + 4; 34 | msg!("begin check"); 35 | loop { 36 | let current_mint_address = &data[position..position + PUBKEY_SIZE]; 37 | let mut is_found = true; 38 | for v in current_mint_address.iter().zip(mint_address.iter()) { 39 | let (v1, v2) = v; 40 | if v1 != v2 { 41 | is_found = false; 42 | break; 43 | } 44 | } 45 | if is_found { 46 | msg!("Mint address found in position {}", position); 47 | return Ok(true); 48 | } 49 | position = position + PUBKEY_SIZE; 50 | if position >= data.len() { 51 | break; 52 | } 53 | } 54 | return Ok(false); 55 | } 56 | 57 | #[program] 58 | pub mod nft_staking { 59 | use spl_token::instruction::AuthorityType::AccountOwner; 60 | use utils::*; 61 | use super::*; 62 | 63 | // initialize staking pool 64 | pub fn initialize_pool( 65 | ctx: Context, 66 | _pool_bump: u8, uuid: String, num_mint: u32, _reward_bump: u8, reward_duration: u64, 67 | ) -> ProgramResult { 68 | if num_mint <= 0 { 69 | return Err(ErrorCode::InsufficientTokenStake.into()); 70 | } 71 | if reward_duration < MIN_DURATION { 72 | return Err(ErrorCode::DurationTooShort.into()); 73 | } 74 | msg!("initializing"); 75 | 76 | let pool_account = &mut ctx.accounts.pool_account; 77 | 78 | if pool_account.is_initialized { 79 | return Err(ProgramError::AccountAlreadyInitialized); 80 | } 81 | pool_account.is_initialized = true; 82 | pool_account.authority = *ctx.accounts.authority.key; 83 | pool_account.paused = true; // initial status is paused 84 | pool_account.config = ctx.accounts.config.key(); 85 | pool_account.reward_mint = *ctx.accounts.reward_mint.to_account_info().key; 86 | pool_account.reward_vault = ctx.accounts.reward_vault.key(); 87 | pool_account.last_update_time = clock::Clock::get().unwrap().unix_timestamp.try_into().unwrap(); 88 | pool_account.reward_rate_per_token = 1; 89 | pool_account.reward_duration = reward_duration; 90 | pool_account.reward_duration_end = 0; 91 | pool_account.token_stake_count = 0; 92 | pool_account.user_count = 0; 93 | 94 | let config = &mut ctx.accounts.config; 95 | config.authority = *ctx.accounts.authority.key; 96 | config.uuid = uuid; 97 | config.num_mint = num_mint; 98 | 99 | Ok(()) 100 | } 101 | 102 | // add nft addresses into Config Account 103 | pub fn add_mint_addresses( 104 | ctx: Context, 105 | mint_addresses: Vec, 106 | index: u32, 107 | ) -> ProgramResult { 108 | let config = &mut ctx.accounts.config; 109 | let account = config.to_account_info(); 110 | let current_count = get_config_count(&account.data.borrow())?; 111 | let mut data = account.data.borrow_mut(); 112 | 113 | let mut fixed_config_lines: Vec = vec![]; 114 | 115 | msg!("current count {}", current_count); 116 | 117 | if index > config.num_mint - 1 { 118 | return Err(ErrorCode::IndexGreaterThanLength.into()); 119 | } 120 | 121 | for line in &mint_addresses { 122 | let address = line.clone(); 123 | fixed_config_lines.push(address) 124 | } 125 | 126 | let as_vec = fixed_config_lines.try_to_vec()?; 127 | 128 | // remove unneeded u32 because we're just gonna edit the u32 at the front 129 | let serialized: &[u8] = &as_vec.as_slice()[4..]; 130 | 131 | let position = CONFIG_SIZE_START + 4 + (index as usize) * PUBKEY_SIZE; 132 | 133 | msg!("position {}", position); 134 | 135 | let array_slice: &mut [u8] = 136 | &mut data[position..position + fixed_config_lines.len() * PUBKEY_SIZE]; 137 | array_slice.copy_from_slice(serialized); 138 | 139 | // plug in new count. 140 | let new_count = (index as usize) + fixed_config_lines.len(); 141 | data[CONFIG_SIZE_START..CONFIG_SIZE_START + 4] 142 | .copy_from_slice(&(new_count as u32).to_le_bytes()); 143 | 144 | Ok(()) 145 | } 146 | 147 | pub fn pause(ctx: Context) -> ProgramResult { 148 | let pool_account = &mut ctx.accounts.pool_account; 149 | pool_account.paused = true; 150 | Ok(()) 151 | } 152 | 153 | pub fn resume(ctx: Context) -> ProgramResult { 154 | let pool_account = &mut ctx.accounts.pool_account; 155 | pool_account.paused = false; 156 | Ok(()) 157 | } 158 | 159 | // add funder 160 | pub fn authorize_funder(ctx: Context, funder_to_add: Pubkey) -> ProgramResult { 161 | // owner cannot be added into funders 162 | if funder_to_add == ctx.accounts.pool_account.authority { 163 | return Err(ErrorCode::FunderAlreadyAuthorized.into()); 164 | } 165 | let funders = &mut ctx.accounts.pool_account.funders; 166 | if funders.iter().any(|x| *x == funder_to_add) { 167 | return Err(ErrorCode::FunderAlreadyAuthorized.into()); 168 | } 169 | let default_pubkey = Pubkey::default(); 170 | if let Some(idx) = funders.iter().position(|x| *x == default_pubkey) { 171 | funders[idx] = funder_to_add; 172 | } else { 173 | return Err(ErrorCode::MaxFunders.into()); 174 | } 175 | Ok(()) 176 | } 177 | 178 | // remove funder 179 | pub fn deauthorize_funder(ctx: Context, funder_to_remove: Pubkey) -> ProgramResult { 180 | if funder_to_remove == ctx.accounts.pool_account.authority { 181 | return Err(ErrorCode::CannotDeauthorizePoolAuthority.into()); 182 | } 183 | let funders = &mut ctx.accounts.pool_account.funders; 184 | if let Some(idx) = funders.iter().position(|x| *x == funder_to_remove) { 185 | funders[idx] = Pubkey::default(); 186 | } else { 187 | return Err(ErrorCode::CannotDeauthorizeMissingAuthority.into()); 188 | } 189 | Ok(()) 190 | } 191 | 192 | pub fn fund(ctx: Context, amount: u64) -> ProgramResult { 193 | let pool_account = &mut ctx.accounts.pool_account; 194 | let nft_quantity = ctx.accounts.config.num_mint; 195 | 196 | let now = clock::Clock::get().unwrap().unix_timestamp.try_into().unwrap(); 197 | 198 | /* 199 | (New funded amount + remaing amount in the pool) / Total NFT quantity / duration (seconds) 200 | */ 201 | if now >= pool_account.reward_duration_end { 202 | msg!("amount {}", amount as u128); 203 | msg!("pool_account.reward_duration {}", pool_account.reward_duration as u128); 204 | msg!("nft_quantity {}", nft_quantity as u128); 205 | 206 | pool_account.reward_rate_per_token = (amount as u128) 207 | .checked_mul(PRECISION) 208 | .unwrap() 209 | .checked_div(pool_account.reward_duration as u128) 210 | .unwrap() 211 | .checked_div(nft_quantity as u128) 212 | .unwrap() 213 | .try_into() 214 | .unwrap(); 215 | msg!("New reward rate per token {} ", pool_account.reward_rate_per_token); 216 | } else { 217 | let remaining = pool_account.reward_duration_end.checked_sub(now).unwrap(); 218 | // remaining reward in the pool = reward rate per token * remaining time * number of token 219 | let leftover = (pool_account.reward_rate_per_token as u128) 220 | .checked_mul(remaining as u128) 221 | .unwrap() 222 | .checked_mul(nft_quantity as u128) 223 | .unwrap().try_into() 224 | .unwrap(); 225 | 226 | msg!("Leftover {} rewards amount in the pool", leftover); 227 | 228 | pool_account.reward_rate_per_token = (amount as u128) 229 | .checked_mul(PRECISION) 230 | .unwrap() 231 | .checked_add(leftover) 232 | .unwrap() 233 | .checked_div(pool_account.reward_duration as u128) 234 | .unwrap() 235 | .checked_div(nft_quantity as u128) 236 | .unwrap() 237 | .try_into() 238 | .unwrap(); 239 | 240 | msg!("New reward rate per token {} ", pool_account.reward_rate_per_token); 241 | } 242 | 243 | // Transfer reward tokens into the vault. 244 | let cpi_ctx = CpiContext::new( 245 | ctx.accounts.token_program.to_account_info(), 246 | anchor_spl::token::Transfer { 247 | from: ctx.accounts.funder_vault.to_account_info(), 248 | to: ctx.accounts.reward_vault.to_account_info(), 249 | authority: ctx.accounts.funder.to_account_info(), 250 | }, 251 | ); 252 | 253 | anchor_spl::token::transfer(cpi_ctx, amount)?; 254 | 255 | pool_account.last_update_time = now; // update last update time as current time 256 | pool_account.reward_duration_end = now.checked_add(pool_account.reward_duration).unwrap(); // refresh the reward end period time 257 | 258 | Ok(()) 259 | } 260 | 261 | // create user 262 | pub fn create_user(ctx: Context, _user_bump: u8, _mint_staked_bump: u8, uuid: String) -> ProgramResult { 263 | let user_account = &mut ctx.accounts.user_account; 264 | user_account.pool = *ctx.accounts.pool_account.to_account_info().key; 265 | user_account.user = *ctx.accounts.user.key; 266 | user_account.reward_earned_claimed = 0; 267 | user_account.reward_earned_pending = 0; 268 | user_account.mint_staked_count = 0; 269 | user_account.uuid = uuid; 270 | user_account.mint_staked = *ctx.accounts.mint_staked.to_account_info().key; 271 | 272 | let mint_staked = &mut ctx.accounts.mint_staked; 273 | mint_staked.pool = *ctx.accounts.pool_account.to_account_info().key; 274 | mint_staked.user_account = *user_account.to_account_info().key; 275 | 276 | let pool_account = &mut ctx.accounts.pool_account; 277 | let now = clock::Clock::get().unwrap().unix_timestamp.try_into().unwrap(); 278 | pool_account.last_update_time = now; 279 | pool_account.user_count = pool_account.user_count.checked_add(1).unwrap(); 280 | 281 | Ok(()) 282 | } 283 | 284 | // staking 285 | pub fn stake(ctx: Context, _mint_staked_bump: u8, uuid: String) -> ProgramResult { 286 | let pool_account = &mut ctx.accounts.pool_account; 287 | if pool_account.paused || !pool_account.is_initialized { 288 | return Err(ErrorCode::PoolPaused.into()); 289 | } 290 | 291 | let config = &mut ctx.accounts.config; 292 | let account = config.to_account_info(); 293 | 294 | // check constraint = config.mint_addresses.iter().any(| x | * x == stake_from_account.mint) 295 | let stake_from_account = &mut ctx.accounts.stake_from_account; 296 | if check_mint_address(&account.data.borrow(), &stake_from_account.mint.to_bytes())? == false { 297 | msg!("Mint address is not stakable!"); 298 | return Err(ErrorCode::InvalidMint.into()); 299 | } 300 | 301 | let now = clock::Clock::get().unwrap().unix_timestamp.try_into().unwrap(); 302 | pool_account.token_stake_count = pool_account.token_stake_count.checked_add(1).unwrap(); 303 | pool_account.last_update_time = now; 304 | 305 | let user_account = &mut ctx.accounts.user_account; 306 | let user_opt = Some(user_account); 307 | 308 | update_rewards( 309 | pool_account, 310 | user_opt, 311 | ).unwrap(); 312 | 313 | // update user account 314 | ctx.accounts.user_account.mint_staked = *ctx.accounts.mint_staked.to_account_info().key; 315 | ctx.accounts.user_account.mint_staked_count = ctx.accounts.user_account.mint_staked_count.checked_add(1).unwrap(); 316 | ctx.accounts.user_account.uuid = uuid; 317 | 318 | // update mint staked 319 | if ctx.accounts.user_account.mint_staked_count == 0 { 320 | // no need to transfer any data from the old account 321 | let mint_staked = &mut ctx.accounts.mint_staked; 322 | mint_staked.pool = *ctx.accounts.pool_account.to_account_info().key; 323 | mint_staked.user_account = *ctx.accounts.user_account.to_account_info().key; 324 | mint_staked.mint_accounts.push(ctx.accounts.stake_from_account.key()); 325 | } else { 326 | // has previous data 327 | let mint_staked = &mut ctx.accounts.mint_staked; 328 | mint_staked.pool = *ctx.accounts.pool_account.to_account_info().key; 329 | mint_staked.user_account = *ctx.accounts.user_account.to_account_info().key; 330 | 331 | let current_mint_staked = &mut ctx.accounts.current_mint_staked; 332 | for mint_address in ¤t_mint_staked.mint_accounts { 333 | mint_staked.mint_accounts.push(*mint_address); 334 | } 335 | mint_staked.mint_accounts.push(ctx.accounts.stake_from_account.key()); 336 | } 337 | 338 | // Transfer token authority 339 | { 340 | let (pool_pda, pool_bump) = Pubkey::find_program_address(&[PREFIX.as_bytes(), 341 | ctx.accounts.pool_account.authority.as_ref(), 342 | ctx.accounts.pool_account.config.as_ref(), 343 | ], ctx.program_id); 344 | let _seeds = &[PREFIX.as_bytes(), 345 | ctx.accounts.pool_account.authority.as_ref(), 346 | ctx.accounts.pool_account.config.as_ref(), 347 | &[pool_bump]]; // need this to sign the pda, match the authority 348 | 349 | let cpi_ctx = CpiContext::new( 350 | ctx.accounts.token_program.to_account_info().clone(), 351 | anchor_spl::token::SetAuthority { 352 | current_authority: ctx.accounts.staker.to_account_info().clone(), 353 | account_or_mint: ctx.accounts.stake_from_account.to_account_info().clone(), 354 | }, 355 | ); 356 | msg!("Calling the token program to transfer authority from staker to pool"); 357 | anchor_spl::token::set_authority(cpi_ctx, AccountOwner, Some(pool_pda))?; 358 | } 359 | 360 | Ok(()) 361 | } 362 | 363 | // unstake 364 | pub fn unstake(ctx: Context, _mint_staked_bump: u8, uuid: String) -> ProgramResult { 365 | let pool_account = &mut ctx.accounts.pool_account; 366 | if pool_account.paused || !pool_account.is_initialized { 367 | return Err(ErrorCode::PoolPaused.into()); 368 | } 369 | 370 | let now = clock::Clock::get().unwrap().unix_timestamp.try_into().unwrap(); 371 | pool_account.last_update_time = now; 372 | pool_account.token_stake_count = pool_account.token_stake_count.checked_sub(1).unwrap(); 373 | 374 | let user_account = &mut ctx.accounts.user_account; 375 | let user_opt = Some(user_account); 376 | update_rewards( 377 | pool_account, 378 | user_opt, 379 | ).unwrap(); 380 | 381 | ctx.accounts.user_account.mint_staked = *ctx.accounts.mint_staked.to_account_info().key; 382 | ctx.accounts.user_account.mint_staked_count = ctx.accounts.user_account.mint_staked_count.checked_sub(1).unwrap(); 383 | ctx.accounts.user_account.uuid = uuid; 384 | 385 | // count of user_account.mint_staked must be >= 1 386 | let mint_staked = &mut ctx.accounts.mint_staked; 387 | mint_staked.pool = *ctx.accounts.pool_account.to_account_info().key; 388 | mint_staked.user_account = *ctx.accounts.user_account.to_account_info().key; 389 | 390 | let current_mint_staked = &mut ctx.accounts.current_mint_staked; 391 | for mint_address in ¤t_mint_staked.mint_accounts { 392 | if mint_address != ctx.accounts.unstake_from_account.to_account_info().key { 393 | mint_staked.mint_accounts.push(*mint_address); 394 | } 395 | } 396 | 397 | // Transfer token authority 398 | { 399 | let (_pool_pda, pool_bump) = Pubkey::find_program_address(&[PREFIX.as_bytes(), 400 | ctx.accounts.pool_account.authority.as_ref(), 401 | ctx.accounts.pool_account.config.as_ref(), 402 | ], ctx.program_id); 403 | let seeds = &[PREFIX.as_bytes(), 404 | ctx.accounts.pool_account.authority.as_ref(), 405 | ctx.accounts.pool_account.config.as_ref(), 406 | &[pool_bump]]; // need this to sign the pda, match the authority 407 | 408 | let cpi_ctx = CpiContext::new( 409 | ctx.accounts.token_program.to_account_info().clone(), 410 | anchor_spl::token::SetAuthority { 411 | current_authority: ctx.accounts.pool_account.to_account_info().clone(), 412 | account_or_mint: ctx.accounts.unstake_from_account.to_account_info().clone(), 413 | }, 414 | ); 415 | msg!("Calling the token program to transfer authority from pool to unstaker"); 416 | anchor_spl::token::set_authority(cpi_ctx.with_signer(&[&seeds[..]]), 417 | AccountOwner, 418 | Some(ctx.accounts.staker.key()))?; 419 | } 420 | 421 | Ok(()) 422 | } 423 | 424 | pub fn claim(ctx: Context) -> ProgramResult { 425 | let pool_account = &mut ctx.accounts.pool_account; 426 | if pool_account.paused || !pool_account.is_initialized { 427 | return Err(ErrorCode::PoolPaused.into()); 428 | } 429 | 430 | let user_account = &mut ctx.accounts.user_account; 431 | let user_opt = Some(user_account); 432 | update_rewards( 433 | pool_account, 434 | user_opt, 435 | ).unwrap(); 436 | 437 | // Transfer rewards from the pool reward vaults to user reward vaults. 438 | let (_pool_pda, pool_bump) = Pubkey::find_program_address(&[PREFIX.as_bytes(), 439 | ctx.accounts.pool_account.authority.as_ref(), 440 | ctx.accounts.pool_account.config.as_ref(), 441 | ], ctx.program_id); 442 | let seeds = &[PREFIX.as_bytes(), 443 | ctx.accounts.pool_account.authority.as_ref(), 444 | ctx.accounts.pool_account.config.as_ref(), 445 | &[pool_bump]]; // need this to sign the pda, match the authority 446 | 447 | if ctx.accounts.user_account.reward_earned_pending > 0 { 448 | let mut reward_amount = ctx.accounts.user_account.reward_earned_pending; 449 | let vault_balance = ctx.accounts.reward_vault.amount; 450 | 451 | // settle pending reward 452 | ctx.accounts.user_account.reward_earned_pending = 0; 453 | ctx.accounts.user_account.reward_earned_claimed = ctx.accounts.user_account.reward_earned_claimed + reward_amount; 454 | 455 | if vault_balance < reward_amount { 456 | reward_amount = vault_balance; 457 | } 458 | 459 | if reward_amount > 0 { 460 | let token_program = ctx.accounts.token_program.clone(); 461 | let token_accounts = anchor_spl::token::Transfer { 462 | from: ctx.accounts.reward_vault.to_account_info().clone(), 463 | to: ctx 464 | .accounts 465 | .reward_to_account 466 | .to_account_info() 467 | .clone(), 468 | authority: ctx.accounts.pool_account.to_account_info().clone(), 469 | }; 470 | let cpi_ctx = CpiContext::new(token_program, token_accounts); 471 | msg!("Calling the token program to transfer reward {} to the user", reward_amount); 472 | anchor_spl::token::transfer( 473 | cpi_ctx.with_signer(&[&seeds[..]]), 474 | reward_amount, 475 | )?; 476 | } 477 | } 478 | 479 | Ok(()) 480 | } 481 | 482 | pub fn close_user(ctx: Context) -> ProgramResult { 483 | let pool_account = &mut ctx.accounts.pool_account; 484 | if pool_account.paused || !pool_account.is_initialized { 485 | return Err(ErrorCode::PoolPaused.into()); 486 | } 487 | 488 | let now = clock::Clock::get().unwrap().unix_timestamp.try_into().unwrap(); 489 | pool_account.last_update_time = now; 490 | pool_account.user_count = pool_account.user_count.checked_sub(1).unwrap(); 491 | 492 | let user_account = &mut ctx.accounts.user_account; 493 | let user_opt = Some(user_account); 494 | update_rewards( 495 | pool_account, 496 | user_opt, 497 | ).unwrap(); 498 | 499 | if ctx.accounts.user_account.mint_staked_count > 0 { 500 | return Err(ErrorCode::StakedMint.into()); 501 | } 502 | 503 | if ctx.accounts.user_account.reward_earned_pending > 0 { 504 | return Err(ErrorCode::PendingRewards.into()); 505 | } 506 | 507 | // ok 508 | Ok(()) 509 | } 510 | 511 | pub fn close_pool(ctx: Context) -> ProgramResult { 512 | // let pool_account = &mut ctx.accounts.pool_account; 513 | 514 | let (_pool_pda, pool_bump) = Pubkey::find_program_address(&[PREFIX.as_bytes(), 515 | ctx.accounts.pool_account.authority.as_ref(), 516 | ctx.accounts.pool_account.config.as_ref(), 517 | ], ctx.program_id); 518 | let seeds = &[PREFIX.as_bytes(), 519 | ctx.accounts.pool_account.authority.as_ref(), 520 | ctx.accounts.pool_account.config.as_ref(), 521 | &[pool_bump]]; // need this to sign the pda, match the authority 522 | 523 | //close reward vault 524 | let token_program = ctx.accounts.token_program.clone(); 525 | let token_accounts = anchor_spl::token::Transfer { 526 | from: ctx.accounts.reward_vault.to_account_info().clone(), 527 | to: ctx.accounts.reward_refundee.to_account_info().clone(), 528 | authority: ctx.accounts.pool_account.to_account_info().clone(), 529 | }; 530 | let cpi_ctx = CpiContext::new(token_program, token_accounts); 531 | msg!("Calling the token program to refund reward"); 532 | anchor_spl::token::transfer( 533 | cpi_ctx.with_signer(&[&seeds[..]]), 534 | ctx.accounts.reward_vault.amount, 535 | )?; 536 | 537 | let token_program = ctx.accounts.token_program.clone(); 538 | let token_accounts = anchor_spl::token::CloseAccount { 539 | account: ctx.accounts.reward_vault.to_account_info().clone(), 540 | destination: ctx.accounts.refundee.to_account_info().clone(), 541 | authority: ctx.accounts.pool_account.to_account_info().clone(), 542 | }; 543 | let cpi_ctx = CpiContext::new(token_program, token_accounts); 544 | msg!("Calling the token program to close reward vault"); 545 | anchor_spl::token::close_account( 546 | cpi_ctx.with_signer(&[&seeds[..]]), 547 | )?; 548 | 549 | Ok(()) 550 | } 551 | } 552 | 553 | #[derive(Accounts)] 554 | #[instruction(pool_bump: u8, uuid: String, num_mint: u32, reward_bump: u8, reward_duration: u64)] 555 | pub struct InitializePool<'info> { 556 | // The pool authority 557 | #[account(mut, signer)] 558 | authority: AccountInfo<'info>, 559 | 560 | // The pool account, it holds all necessary info about the pool 561 | #[account(init, 562 | seeds = [PREFIX.as_bytes(), authority.key.as_ref(), config.key().as_ref()], 563 | bump = pool_bump, 564 | payer = authority, 565 | space = POOL_SIZE)] 566 | pool_account: ProgramAccount<'info, Pool>, 567 | 568 | // the config account holds the information of the nft token that can be staked 569 | // the number of mint to stake is pre-determined when the pool is set up 570 | #[account(zero)] 571 | config: ProgramAccount<'info, Config>, 572 | 573 | // reward mint 574 | reward_mint: AccountInfo<'info>, 575 | 576 | // reward vault that holds the reward mint for distribution 577 | #[account(init, 578 | token::mint = reward_mint, 579 | token::authority = pool_account, 580 | seeds = [PREFIX.as_bytes(), 581 | pool_account.key().as_ref(), 582 | authority.key.as_ref(), 583 | reward_mint.key.as_ref()], 584 | bump = reward_bump, 585 | payer = authority 586 | )] 587 | reward_vault: Box>, 588 | 589 | // The rent sysvar 590 | rent: Sysvar<'info, Rent>, 591 | 592 | // The Token Program 593 | #[account(address = spl_token::id())] 594 | token_program: AccountInfo<'info>, 595 | 596 | // system program 597 | system_program: Program<'info, System>, 598 | } 599 | 600 | #[derive(Accounts)] 601 | pub struct AddMintAddresses<'info> { 602 | #[account(mut, signer)] 603 | authority: AccountInfo<'info>, 604 | 605 | // Pool Account 606 | #[account(mut, 607 | constraint = pool_account.is_initialized == true, 608 | constraint = pool_account.config == * config.to_account_info().key, 609 | has_one = authority, 610 | )] 611 | pool_account: ProgramAccount<'info, Pool>, 612 | 613 | #[account(mut, has_one = authority)] 614 | config: ProgramAccount<'info, Config>, 615 | } 616 | 617 | #[derive(Accounts)] 618 | pub struct Pause<'info> { 619 | #[account(mut, signer)] 620 | authority: AccountInfo<'info>, 621 | 622 | // Pool Account 623 | #[account(mut, 624 | constraint = pool_account.is_initialized == true, 625 | )] 626 | pool_account: ProgramAccount<'info, Pool>, 627 | } 628 | 629 | #[derive(Accounts)] 630 | pub struct FunderChange<'info> { 631 | #[account(mut, signer)] 632 | authority: AccountInfo<'info>, 633 | 634 | // Pool Account 635 | #[account(mut, 636 | constraint = pool_account.is_initialized == true, 637 | has_one = authority, 638 | )] 639 | pool_account: ProgramAccount<'info, Pool>, 640 | } 641 | 642 | #[derive(Accounts)] 643 | pub struct Fund<'info> { 644 | // funder 645 | // verify in the funders list 646 | #[account(mut, signer, 647 | constraint = funder.key() == pool_account.authority || pool_account.funders.iter().any(| x | * x == funder.key()),)] 648 | funder: AccountInfo<'info>, 649 | 650 | // Pool owner 651 | authority: AccountInfo<'info>, 652 | 653 | // Pool Account 654 | // verify pool is not paused 655 | // verify owner 656 | // verify token vault 657 | #[account(mut, 658 | has_one = authority, 659 | constraint = pool_account.is_initialized == true, 660 | constraint = pool_account.paused == false, 661 | constraint = pool_account.reward_vault == * reward_vault.to_account_info().key, 662 | constraint = pool_account.config == * config.to_account_info().key, 663 | )] 664 | pool_account: ProgramAccount<'info, Pool>, 665 | 666 | // the config account 667 | #[account(has_one = authority)] 668 | config: ProgramAccount<'info, Config>, 669 | 670 | #[account(mut)] 671 | reward_vault: Box>, 672 | 673 | // funder vault 674 | #[account(mut)] 675 | funder_vault: Account<'info, TokenAccount>, 676 | 677 | // The Token Program 678 | #[account(address = spl_token::id())] 679 | token_program: AccountInfo<'info>, 680 | 681 | } 682 | 683 | #[derive(Accounts)] 684 | #[instruction(user_bump: u8, mint_staked_bump: u8, uuid: String)] 685 | pub struct CreateUser<'info> { 686 | // user owner 687 | #[account(mut, signer)] 688 | user: AccountInfo<'info>, 689 | 690 | // Pool Account 691 | #[account(mut, 692 | constraint = pool_account.is_initialized == true, 693 | constraint = pool_account.paused == false, 694 | )] 695 | pool_account: ProgramAccount<'info, Pool>, 696 | 697 | // User account where the user info is stored 698 | #[account( 699 | init, 700 | payer = user, 701 | seeds = [ 702 | PREFIX_USER.as_bytes(), 703 | pool_account.to_account_info().key.as_ref(), 704 | user.key.as_ref(), 705 | ], 706 | bump = user_bump, 707 | space = USER_SIZE)] 708 | user_account: ProgramAccount<'info, User>, 709 | 710 | // new mint staked account 711 | #[account( 712 | init, 713 | payer = user, 714 | seeds = [ 715 | PREFIX_MINT.as_bytes(), 716 | pool_account.to_account_info().key.as_ref(), 717 | user_account.to_account_info().key.as_ref(), 718 | uuid.as_bytes(), 719 | ], 720 | bump = mint_staked_bump, 721 | space = MINT_STAKED_SIZE_START)] 722 | mint_staked: ProgramAccount<'info, MintStaked>, 723 | 724 | // The rent sysvar 725 | rent: Sysvar<'info, Rent>, 726 | 727 | system_program: Program<'info, System>, 728 | } 729 | 730 | #[derive(Accounts)] 731 | #[instruction(mint_staked_bump: u8, uuid: String)] 732 | pub struct Stake<'info> { 733 | #[account(mut, signer)] 734 | staker: AccountInfo<'info>, 735 | 736 | // Pool Account 737 | // verify pool is not paused 738 | // verify owner 739 | // verify config 740 | #[account(mut, 741 | has_one = authority, 742 | constraint = pool_account.is_initialized == true, 743 | constraint = pool_account.paused == false, 744 | constraint = pool_account.config == * config.to_account_info().key, 745 | )] 746 | pool_account: ProgramAccount<'info, Pool>, 747 | 748 | // the config account 749 | #[account(mut, has_one = authority)] 750 | config: ProgramAccount<'info, Config>, 751 | 752 | // Pool owner 753 | authority: AccountInfo<'info>, 754 | 755 | // user account 756 | // verify owner is the signer 757 | // verify pool is the pool account 758 | #[account( 759 | mut, 760 | constraint = user_account.pool == * pool_account.to_account_info().key, 761 | constraint = user_account.user == * staker.key, 762 | constraint = user_account.mint_staked == * current_mint_staked.to_account_info().key, 763 | )] 764 | user_account: ProgramAccount<'info, User>, 765 | 766 | // account to stake from 767 | // since we added the nft addresses in config.mint_addresses, this is to check if the mint address in the stake_from_account in the config.mint_addresses 768 | // constraint = config.mint_addresses.iter().any(| x | * x == stake_from_account.mint) 769 | #[account(mut, 770 | )] 771 | stake_from_account: Box>, 772 | 773 | // new mint staked account to store all the mint staked for the user 774 | // this will keep the information of the token account that holds the nft as an ownership transfer of the whole account will occur in stake/unstake 775 | #[account( 776 | init, 777 | payer = staker, 778 | seeds = [ 779 | PREFIX_MINT.as_bytes(), 780 | pool_account.to_account_info().key.as_ref(), 781 | user_account.to_account_info().key.as_ref(), 782 | uuid.as_bytes(), 783 | ], 784 | bump = mint_staked_bump, 785 | space = MINT_STAKED_SIZE_START + 32 * (user_account.mint_staked_count + 1) as usize)] 786 | mint_staked: ProgramAccount<'info, MintStaked>, 787 | 788 | // existing mint staked account 789 | #[account(mut, 790 | constraint = current_mint_staked.pool == * pool_account.to_account_info().key, 791 | constraint = current_mint_staked.user_account == * user_account.to_account_info().key, 792 | close = staker, 793 | )] 794 | current_mint_staked: ProgramAccount<'info, MintStaked>, 795 | 796 | // The rent sysvar 797 | rent: Sysvar<'info, Rent>, 798 | 799 | // The Token Program 800 | #[account(address = spl_token::id())] 801 | token_program: AccountInfo<'info>, 802 | 803 | // system program 804 | system_program: Program<'info, System>, 805 | } 806 | 807 | #[derive(Accounts)] 808 | #[instruction(mint_staked_bump: u8, uuid: String)] 809 | pub struct Unstake<'info> { 810 | #[account(mut, signer)] 811 | staker: AccountInfo<'info>, 812 | 813 | // Pool Account 814 | // verify pool is not paused 815 | // verify owner 816 | // verify config 817 | #[account(mut, 818 | has_one = authority, 819 | constraint = pool_account.is_initialized == true, 820 | constraint = pool_account.paused == false, 821 | constraint = pool_account.config == * config.to_account_info().key, 822 | )] 823 | pool_account: ProgramAccount<'info, Pool>, 824 | 825 | // the config account 826 | #[account(mut, has_one = authority)] 827 | config: ProgramAccount<'info, Config>, 828 | 829 | // Pool owner 830 | authority: AccountInfo<'info>, 831 | 832 | // user account 833 | // verify owner is the signer 834 | // verify pool is the pool account 835 | #[account( 836 | mut, 837 | constraint = user_account.pool == * pool_account.to_account_info().key, 838 | constraint = user_account.user == * staker.key, 839 | constraint = user_account.mint_staked == * current_mint_staked.to_account_info().key, 840 | )] 841 | user_account: ProgramAccount<'info, User>, 842 | 843 | // The nft token account to unstake 844 | #[account(mut)] 845 | unstake_from_account: Box>, 846 | 847 | // new mint staked account to store all the mint staked for the user 848 | #[account( 849 | init, 850 | payer = staker, 851 | seeds = [ 852 | PREFIX_MINT.as_bytes(), 853 | pool_account.to_account_info().key.as_ref(), 854 | user_account.to_account_info().key.as_ref(), 855 | uuid.as_bytes(), 856 | ], 857 | bump = mint_staked_bump, 858 | space = MINT_STAKED_SIZE_START + 32 * (user_account.mint_staked_count + 1) as usize)] 859 | mint_staked: ProgramAccount<'info, MintStaked>, 860 | 861 | // existing mint staked account 862 | // verify the unstake token account is in the mint staked 863 | #[account(mut, 864 | constraint = current_mint_staked.pool == * pool_account.to_account_info().key, 865 | constraint = current_mint_staked.user_account == * user_account.to_account_info().key, 866 | constraint = current_mint_staked.mint_accounts.iter().any(| x | * x == * unstake_from_account.to_account_info().key), 867 | close = staker, 868 | )] 869 | current_mint_staked: ProgramAccount<'info, MintStaked>, 870 | 871 | // The rent sysvar 872 | rent: Sysvar<'info, Rent>, 873 | 874 | // The Token Program 875 | #[account(address = spl_token::id())] 876 | token_program: AccountInfo<'info>, 877 | 878 | // system program 879 | system_program: Program<'info, System>, 880 | } 881 | 882 | #[derive(Accounts)] 883 | pub struct ClaimReward<'info> { 884 | #[account(mut, signer)] 885 | user: AccountInfo<'info>, 886 | 887 | // Pool Account 888 | // verify pool is not paused 889 | // verify owner 890 | // verify config 891 | #[account(mut, 892 | has_one = authority, 893 | constraint = pool_account.is_initialized == true, 894 | constraint = pool_account.paused == false, 895 | )] 896 | pool_account: ProgramAccount<'info, Pool>, 897 | 898 | // Pool owner 899 | authority: AccountInfo<'info>, 900 | 901 | #[account(mut)] 902 | reward_vault: Box>, 903 | 904 | // user account 905 | // verify owner is the signer 906 | // verify pool is the pool account 907 | #[account( 908 | mut, 909 | constraint = user_account.pool == * pool_account.to_account_info().key, 910 | constraint = user_account.user == * user.key, 911 | )] 912 | user_account: ProgramAccount<'info, User>, 913 | 914 | // send reward to user reward vault 915 | #[account(mut)] 916 | reward_to_account: Box>, 917 | 918 | // The Token Program 919 | #[account(address = spl_token::id())] 920 | token_program: AccountInfo<'info>, 921 | } 922 | 923 | #[derive(Accounts)] 924 | pub struct CloseUser<'info> { 925 | // user owner 926 | #[account(mut, signer)] 927 | user: AccountInfo<'info>, 928 | 929 | // Pool Account 930 | #[account(mut, 931 | constraint = pool_account.is_initialized == true, 932 | constraint = pool_account.paused == false, 933 | )] 934 | pool_account: ProgramAccount<'info, Pool>, 935 | 936 | // user account 937 | // user has to unstake everything and claim everything (check in process) before close account 938 | #[account( 939 | mut, 940 | constraint = user_account.pool == * pool_account.to_account_info().key, 941 | constraint = user_account.user == * user.key, 942 | close = user, 943 | )] 944 | user_account: ProgramAccount<'info, User>, 945 | } 946 | 947 | #[derive(Accounts)] 948 | pub struct ClosePool<'info> { 949 | // authority 950 | #[account(mut, signer)] 951 | authority: AccountInfo<'info>, 952 | 953 | // Pool Account 954 | #[account(mut, 955 | close = refundee, 956 | constraint = pool_account.is_initialized == true, 957 | constraint = pool_account.paused == false, 958 | constraint = pool_account.authority == * authority.key, 959 | constraint = pool_account.reward_vault == * reward_vault.to_account_info().key, 960 | constraint = pool_account.reward_duration_end > 0, 961 | constraint = pool_account.reward_duration_end < clock::Clock::get().unwrap().unix_timestamp.try_into().unwrap(), 962 | constraint = pool_account.token_stake_count == 0, 963 | constraint = pool_account.user_count == 0, 964 | )] 965 | pool_account: ProgramAccount<'info, Pool>, 966 | 967 | // the config account 968 | #[account(mut, 969 | has_one = authority, 970 | )] 971 | config: ProgramAccount<'info, Config>, 972 | 973 | #[account(mut)] 974 | refundee: AccountInfo<'info>, 975 | // balance will go to 976 | #[account(mut)] 977 | reward_refundee: Box>, 978 | 979 | #[account(mut)] 980 | reward_vault: Box>, 981 | 982 | // The Token Program 983 | #[account(address = spl_token::id())] 984 | token_program: AccountInfo<'info>, 985 | 986 | } 987 | 988 | 989 | pub const POOL_SIZE: usize = 8 + // discriminator 990 | 1 + // is_initialized 991 | 32 + // authority 992 | 1 + // paused 993 | 32 + // config 994 | 32 + // reward_mint 995 | 32 + // reward_vault 996 | 8 + // last_update_time 997 | 16 + // reward_per_token 998 | 8 + // reward_duration 999 | 8 + // reward_duration_end 1000 | 4 + // token_stake_count 1001 | 4 + // user_count 1002 | 4 + 32 * 5; // funders 1003 | 1004 | #[account] 1005 | #[derive(Default)] 1006 | pub struct Pool { 1007 | // 1 1008 | pub is_initialized: bool, 1009 | /// authority (owner) pubkey 1010 | pub authority: Pubkey, 1011 | /// Paused state of the program 1012 | pub paused: bool, 1013 | /// Config Account that stores all the nft token that can be staked 1014 | pub config: Pubkey, 1015 | /// Mint of the reward token. 1016 | pub reward_mint: Pubkey, 1017 | /// Vault to store reward tokens. 1018 | pub reward_vault: Pubkey, 1019 | /// The last time reward states were updated. 1020 | pub last_update_time: u64, 1021 | /// Reward per token per time unit 1022 | pub reward_rate_per_token: u128, 1023 | /// Reward duration 1024 | pub reward_duration: u64, 1025 | /// Reward duration end 1026 | pub reward_duration_end: u64, 1027 | /// Tokens Staked 1028 | pub token_stake_count: u32, 1029 | /// User created 1030 | pub user_count: u32, 1031 | /// authorized funders 1032 | pub funders: [Pubkey; 5], 1033 | } 1034 | 1035 | 1036 | pub const CONFIG_SIZE_START: usize = 8 + // discriminator 1037 | 32 + // authority 1038 | 4 + 6 + // uuid + u32 le 1039 | 4; // num_mint 1040 | 1041 | #[account] 1042 | #[derive(Default)] 1043 | pub struct Config { 1044 | /// authority (owner) pubkey 1045 | pub authority: Pubkey, 1046 | /// uuid 1047 | pub uuid: String, 1048 | /// number of token addresses that can be staked 1049 | pub num_mint: u32, 1050 | } 1051 | 1052 | pub const USER_SIZE: usize = 8 + // discriminator 1053 | 32 + // pool 1054 | 32 + // user 1055 | 8 + // reward_per_token_complete 1056 | 8 + // reward_per_token_pending 1057 | 4 + // mint_staked_count 1058 | 4 + 6 + // uuid + u32 le 1059 | 32 + // mint_staked 1060 | 8; //last update time 1061 | 1062 | // 32 + 32 + 128 + 64 + 32 1063 | #[account] 1064 | #[derive(Default)] 1065 | pub struct User { 1066 | /// Pool the this user belongs to. 1067 | pub pool: Pubkey, 1068 | /// The user 1069 | pub user: Pubkey, 1070 | /// The total amount of reward claimed 1071 | pub reward_earned_claimed: u64, 1072 | /// The total amount of reward pending 1073 | pub reward_earned_pending: u64, 1074 | /// mint staked count 1075 | pub mint_staked_count: u32, 1076 | /// uuid for generating the mint_staked program account for this user 1077 | pub uuid: String, 1078 | /// The mint_staked, hold information all the token account addresses that hold the mint 1079 | pub mint_staked: Pubkey, 1080 | //last update time for stake/unstake 1081 | pub last_update_time: u64, 1082 | } 1083 | 1084 | pub const MINT_STAKED_SIZE_START: usize = 8 + // discriminator 1085 | 32 + // pool 1086 | 32 + // user_account 1087 | 4; // u32 len for Vec 1088 | 1089 | #[account] 1090 | #[derive(Default)] 1091 | pub struct MintStaked { 1092 | /// Pool 1093 | pub pool: Pubkey, 1094 | /// User account 1095 | pub user_account: Pubkey, 1096 | /// mint addresses 1097 | pub mint_accounts: Vec, // theroctically account can hold (10,000,000 - 32 - 32)/32 = 312_497 mint addresses 1098 | } 1099 | 1100 | #[error] 1101 | pub enum ErrorCode { 1102 | #[msg("Insufficient tokens to stake.")] 1103 | InsufficientTokenStake, 1104 | #[msg("Insufficient funds to stake.")] 1105 | InsufficientFundStake, 1106 | #[msg("Insufficient funds to unstake.")] 1107 | InsufficientFundUnstake, 1108 | #[msg("Amount must be greater than zero.")] 1109 | AmountMustBeGreaterThanZero, 1110 | #[msg("Reward B cannot be funded - pool is single stake.")] 1111 | SingleStakeTokenBCannotBeFunded, 1112 | #[msg("Pool is paused or is not initialized.")] 1113 | PoolPaused, 1114 | #[msg("User has staked mint")] 1115 | StakedMint, 1116 | #[msg("User has pending rewards.")] 1117 | PendingRewards, 1118 | #[msg("Duration cannot be shorter than one day.")] 1119 | DurationTooShort, 1120 | #[msg("Provided funder is already authorized to fund.")] 1121 | FunderAlreadyAuthorized, 1122 | #[msg("Maximum funders already authorized.")] 1123 | MaxFunders, 1124 | #[msg("Cannot deauthorize the primary pool authority.")] 1125 | CannotDeauthorizePoolAuthority, 1126 | #[msg("Authority not found for deauthorization.")] 1127 | CannotDeauthorizeMissingAuthority, 1128 | #[msg("Index greater than length!")] 1129 | IndexGreaterThanLength, 1130 | #[msg("Numerical overflow error!")] 1131 | NumericalOverflowError, 1132 | #[msg("Mint address is not stakable!")] 1133 | InvalidMint, 1134 | } -------------------------------------------------------------------------------- /client/token_mints.json: -------------------------------------------------------------------------------- 1 | [ 2 | "2Egfg3FNg8CCfx7dWM7z9vjDNPtjVHR8cu694sjWkyoT", 3 | "9NMGRKpCP2uVWyLAR7thEGie2hV7exTZaBe4GeWw1r7E", 4 | "3RhwhGgMVXXfS7DArWdtFuWUELnmuCR1TWZjoCBkLmrV", 5 | "GCnqb2LWuFhUZz4JvJ46JxTomunDELKbcudUkSup5ArT", 6 | "59oT5KdACuUVdtNdKKL88porw1AN9inkrcMCMBu1rkcb", 7 | "8yfeTPo7i4XkssuYswHbkBAxmq1xzowmr5quNJYDfEHr", 8 | "7rwtTES6fbhKtDf5ZN5JFMJbWXo3u2329XfmSTmVTcMT", 9 | "FTgxMFDY6YRNSsYt2qLA1LMdBY6MMQiHpbFHRfEJ5neT", 10 | "6mSqB2XYqYLxRbxFQVmksQfKyt4QTdLeqWsRXiGifRXf", 11 | "C84bTMMvjtewfPwbv4uKF1XaduifSywCQ8BT8vCoeB5k", 12 | "893mCBdp3ToavWJvdJvE7TDm5qCbKZ9ke7Gx5fobnBx4", 13 | "2FU2ocrCAEsuugPNo8SD76xou5d1mP1CzSaNkGpS9Npy", 14 | "HwRQrHsrzW9VCY3wo4op6TWAxJHxu4FuxnwQaD5nuTtE", 15 | "9ToiKsbjyNCffszVHsqdjiLVyjhvpPTGxoduNJucDnBU", 16 | "Zm6xNQ3PXXacj5gAd9udZ8zaHegMvyiQ9JJHvfcswLF", 17 | "FGvpuYy6cdz6eQ5vvQ6sG5RxNPpZ2D9dqxcLR9JCexjL", 18 | "7h13nJxVi4Sy5vdgWxV8yMJcwt4EXVb5rRM17Qk3TDog", 19 | "SJ9FWBULJvSX6oe4TeTScVG9YBC9MMgjMCSBHYGgoaY", 20 | "7XwFFPkLyKyNbtnez4Yu9q1d2SXJ6JCepDE5bdEgscUA", 21 | "4niM6tQUrAnNNKteyfoM1BhUZAMzYYfujRUqCQXv5hzN", 22 | "7vkzE1vQ6jZeGWz9SN8ZYJKc2m2gpzQ6wDx8pThRqvon", 23 | "BCiJsapgUvo8WbiEhtWQ1wQLHnpLLYb6QNbY36YbciHe", 24 | "9yFbb6HLkSxkgtBv9KP6L5eSNk6sh5BYRfihU6ajwaX1", 25 | "54EPQj3zZSAAWditP4DaPBy9iY9oVyJCCa78JEEf7xUd", 26 | "HvKdLhyxADvZBCpNFyPEHwUyzhN8SEtnrKSpmGQRhru4", 27 | "J33ji5mhT3p7L6GAjh5e2MjpUE31sBr6S78qWfLBytdK", 28 | "FKTHzxEkGiKnJqtv9y9ZjUdCEo2MaWEpvkq68aDVTnGY", 29 | "6DcothjhBJFHU1ixbv6nuxz6P6uSst6UBP1Y5Q9RJrSt", 30 | "AVTDARZjPMDFy1feaH8qyb27WT5SWVd7LzDancNubJxi", 31 | "GroJKJBTFBJXrKGkH79q9G8mgponiZPSz6YwqrspZzXT", 32 | "H2NovEp8EpDENXDJfKijL9kCDX4oo9imkaSQiREMT8iq", 33 | "9ax1Fgjk45YowqHFHY2ceXc9WpEkrCLzUdmA8qb11KRE", 34 | "8Tf7t4o1sXN744xHK1R6MNtWpncbRFe6F9HbBD4yanaj", 35 | "AEseqyK1uue56q9T7TzagenuBm8pihqZPaXxSQ16Wac9", 36 | "HkW8GNDtFXVs4txqqoSGgU31LEhsbjXSRTca2hKk1Jpq", 37 | "6tsScKvj3EHwrdtszz2assXQ3VcP2DRohXhwoRUevyYJ", 38 | "CsPfduufMxXXKzc7VGcTyEaudtDPaKTSMmS5A8eUHaMD", 39 | "GxQ8bGCbdiYCkBCMzJZpkFHwqFPp5bWVPCzumZubJcJN", 40 | "C3U6tHEBeDqiAVwA48S9jWmvgcAu8nnwRhTCSexV5uwh", 41 | "4Zc23MhZrgoiaM7ZMrojWocAPcSpyNg6pAB18Xomompz", 42 | "7Gt72Qb5HxGZwk6zLAQibjvAPxXybcLfXavHf8sRYNWv", 43 | "EVhAXw1vm8XpabuL2c4sRaJJ9g5hkaT6TdRnkpFfkpHM", 44 | "3qGtRxDHnbZtLQByxYioatk2yhMt9zfzU5yz46PS9DJd", 45 | "B8HadN926Yp1CefDnWzGngjv3jXr4kqg8ZAfyxp2s9cP", 46 | "Eh5o9zrvk7VtHUCf4GX9brKEKk966rWAi9m2WFm8MKeD", 47 | "7LaH3pjKtKb3o2L7qqTYMQ5w4Rdo3ivHfgMjD4cWGkEM", 48 | "WAdzw2upcV74MpLovX8t1SA6yN8ekNoPmJ9z8nnGCG2", 49 | "7eDA526W3gsKDWyP7ZbA8suC96LGk4xXqQ444Qkf3rp6", 50 | "A6KZuWCrajdyuHzWCjPka8fRBFKpxTFq6ojZdxxgQyeo", 51 | "H9qdBNQ1iStBEwu5inbfEcGeNjbjxw5TCxdd8JGqw9Ey", 52 | "Eas6ZmC4Zy159DHudHoqaTb2zmzuo4X15kt4MECGsEz8", 53 | "9LSMK12FSap2h1qSiqeQWLRcuDdxp33zN6bsHN3qYD5Y", 54 | "9ZTsVXVrEYMgoVvJYEKozVBED5iuWRAHZRzXoNsPYXsw", 55 | "AE7gAb9n66rgYLgPxCjj71JEK7BkRwtdj99CRrvYJdw3", 56 | "E4GAjFWU32zjk17CcQpRehnbndbowxti8zi6YJyw4mzh", 57 | "52pKHNSVSiwHhynL8vdoXNy1StUr5tGeEdmP1QZf4c2J", 58 | "Gk3Fnx15GWpC7w2rhNy1QGqNtNpcx3fJrKt5EXsyp9Nf", 59 | "7ApwFpVuUt8pSzAHCzSNZXCTzbfgT7ct5dSYofSXC2Ap", 60 | "2YdAiTMTzExWhDKEidVQAStbjWUJLCkQSX4fZksTr6hZ", 61 | "Bdi9mCPLvrN2BdeXLLp1rs7dsoikFwxSQHBdBZ3Hm5VG", 62 | "AnM6yQPYUS4xCPefThC31jUtMqZyDDRGjfneGJo8pKnW", 63 | "DCEmbrpjnvFYvaKysNU8hkttBbJc1oy5yWtm1TqzwFo7", 64 | "3oNmvbMhL2Usztqgg58J8uy51R6AbGmtwp5UHADWz6nc", 65 | "8f59niKEt9xQYBagjAQD7Zgc7aPnwaZAQFgXFvfJZH6h", 66 | "Bzd3tx9K1avp3ic45kk9WwwimQkPygfzayJop6oPnDpL", 67 | "5pvrUvXGuQGgdKJxTitDDyC3PyxfDWQv9DtTMttWVgZq", 68 | "HsAZtcdtZYL4d83tRi69YPpuDnXNnt3EhHzrFSk4yFme", 69 | "HGXeWveMTLwA9F16VQAWTB3jEDifMg1KBubRZS6Zk9ML", 70 | "2ypQLqQ3tfzWTsa6uh2wd9bpHaPjxa1tLrRxcdszn9P5", 71 | "2ScCbBwg7geYjVgj4ZVbXc5N5midrBfyz8e6YG8ZnHmg", 72 | "GgK4fRYZwqJNVE6UhLZHGzr6DgThnSMQpffCNxW8FMZa", 73 | "3qcP14RFEKQyP8r5A6zjReCrXCUjt5McQgPkBCrgwr9b", 74 | "CPXzn7pdULzjdj8MkbioKGrXHGnEJ3pUbHfMoT6hWw4j", 75 | "3ZKtWRt2uh6E3CzLKRbg1JGcUcrJaUW2gXTBNXhjFuKB", 76 | "FRE8obsbGGncpKajoWg6D7ueot1ajLQpR5zCe4E6FT53", 77 | "HqE6ALY1zbGgG28hfkR6fW55HpsW6qJepn2dKHgFgrEx", 78 | "5qpCFXXci2VFc1xfzdzVACnDxsdrVwnQb7RL87uyVzqU", 79 | "4EdJtBMMuhCn1B52Fsr9m9Fww2gBv9puULAbTdWyVEKy", 80 | "5xcmE4Nq6xLMGsscYakWmggpE2q2ZoxC8DoDvFpsmEut", 81 | "2ugZd9AsWy5BPXKG61EceRHyeoJ1yv55Zs18hqcE4cKm", 82 | "7wDdKzDz6vuHU8RVQxid5TMCXsjhCvJm5sR5cvXyfTDz", 83 | "4PpqPtRup2msVBSSToDMRaQYj4W5MMLspjZqKRJzCNF7", 84 | "7VDGGxJRsHqCcUPdtu63bwnuaKi6spEsdooS1jUvureQ", 85 | "EL36KnHZkX3js7LidrytoM96xzWJLkpPTxwWcTg3DX8d", 86 | "4wnN7XnBKcLUsTpemZvrq9KvDwve7FcQyoVsvNTTPXoD", 87 | "Hg8PQ16PsrFvhAwW2FxQRLqtbzfka93mkbmeEJneMk59", 88 | "93fUzeHtuv6dk3HvyoAfKiE2y8XdjPrZm7W7KfQi4fN1", 89 | "6UGPBB6jQqTCdJAoDU3JoPQzA2j1eaVx66hsA6jrCnAA", 90 | "EuNLDY46ZA1a1er355262dBKQb6e7vjgiaPP8qfSi29g", 91 | "DG6TBh9riNWEGnyrM8NCsJUzxw63ZKNCFLB56pid6n2d", 92 | "BerG5f84CYTyGnQeR7qFKVsyESQnCCt4acVhDL3RXnuA", 93 | "6ZBGhkyJFg495GXDCM8uGsWs7DAw9yxQ2Gifsg67eht3", 94 | "5S8c7bzAdSuoZZeH8xwRGauhdm1kC2R3nmjEdQNeuWBe", 95 | "FJG9ny3vmygTSyyA993nCzWYSionjeGe499de1RuGvRL", 96 | "6iBVDQpSEL79JBtCuiPaaHoZedS6UT3D9QDA3ZXBiS2Y", 97 | "BY3dNPxG4oncBKpNDDksk6y5g3952MVB9Byyqjai9h1u", 98 | "4cEGWWvzveTr7CR6gD33DmJ1xiHhA3Z9GgFYaFTAhLKM", 99 | "FYXwohXCb82ENMNyaea6Ln2YruP2Jb4f6icBbaX5JDTe", 100 | "3R7z5JWDfCnFnE6ihDTBrtzXVDCzinXYyp4Y8P6B2Utv", 101 | "6C9X6X3ovoxNvBew8WJR5AfnafabW1Z8b2tN9udzqzLu", 102 | "J8YRDpagFNoGmCfBBq8vy8F4QuSKpucc4DcmpLTocdbb", 103 | "7E8ZWJJE7fPm4md5Urw1Pm2LPdnXPt4ogF8yazUppJss", 104 | "GE7k6NWHXqBM4xy3puDKMZz6pGdGNLpQJnLhqJ8HwyW6", 105 | "Agdv3NLpeSiWjsaBP4zYZbApGSqvhYi3EvhepYFm9ytv", 106 | "CUsuU5ZYMxR9HkG1AB8cCf59S3NhwW5gRdGtxCaA2kRM", 107 | "DbB1M73PbTt4CZ39r7BCFNFPkEbxpUkVvdhP2ntoVKac", 108 | "BGpqrMC9YurVzMaSVgRQ4WugeDib1UfwZBcDzjzcLEv2", 109 | "C7c5KpfoyzFunomXgVvhmANqQcP7hYhTi5vfFwj2Gu2", 110 | "4ULGUM1CfAJwPbqRS8ZTzzekCeXVLMPmEHfdJFwAdyyZ", 111 | "Bx9XSxrGNK4H4L4au3V8W1Rw7v8y7d3q4tYYAKnNe4gS", 112 | "2BYDhDEN4DhQRwrNUJhXbatEcbBBkdscFX3TvQJWs5M3", 113 | "8iXREQLtigPHbAZWv4Gr33BnyAn55u4Mk3yLifTgjViR", 114 | "FhPErZuq91T2hNmZExzmXik1hyrgBruQhQ7xx3uYojkz", 115 | "3mUeDoxSzSpBudaWVygwszkVSHUkeJ7q4C5DGjhi9twL", 116 | "HEmDevdLtZJJNHguebzfVFY6p5JpHQaX1RnBND1Bg9eB", 117 | "A7GUpU9fUCcG9eRsYxr1ryQTiqPEGMD3baR4ugDy5nXM", 118 | "F2N9EwnC2Fcs6qRyikrTKhRxkTRXGWPHH9feMMNMiquz", 119 | "3c7r6ZtTDxN1fxc1ixM8Ay26hTyfnucs3yx42vVH4LM3", 120 | "kg2mhLoNQy6kjkTyhBfMh7mtv7ecT5WXbTNDwJqq6M8", 121 | "HnoRWRFisKv1ZkHxSjsdSKcr1fytWmgz4k1LD6oemGb7", 122 | "CwxrMxVujaqRehvbB2xHq8HzXXoe319TApkpkSxJjVpn", 123 | "7wqKTDufYPpLyVbYQFRVcUayBtJf98xg9z5S2QjxyVYk", 124 | "8sUq8NxVjRCwdicS5GyiG4Zck9gFPVyxNtYEoJjyu75i", 125 | "AkKvVpEQEcCe2jRw6PrHaexGudZHbKbMjuHa5U9L6C3Z", 126 | "2YQxL2eQrn28hfqtZqQ3SX7cfFyoBhHTALHMKX6xNgvY", 127 | "5ZtdfmbdbZi6F3YhHhziybraGj6oTGdwzTuKXZ7fXFDA", 128 | "CGLXuCGJdZ2BLeg6JGapDwGnwRyqaZLgFvpdRZw5AMRY", 129 | "CSEP5TaKcmf5YiQWhvPUQSV7NFKKEfWXb67UoUoSDA2y", 130 | "AoZU5ZvFbW2WAaWWUWGDny7NspuJfzU4GmVM6Z9Lmkz8", 131 | "3CS35DJsKYV4yRsqnTNgyvAJqyoFRiJvQcbYyXdSQyv7", 132 | "dprgidWnhFuxpjQPw6E2vsz2MSa7sprYy1KNbJSnd7T", 133 | "B98FbTnrnEVQnvKdVR1jRUePxinmq4Jv7z5wdsZJi1RZ", 134 | "2kpDzFwEvJ3k3Rkg786UrxyVedmiCEbcQQU2RFGPFLZt", 135 | "8H6rofiJUtJJng8uQ25MXnWpQQKE66rboAjhhzhnL2rh", 136 | "291h5yiS16B9f8QZm8uQ2pUtEgqNrXV9YmwATzgKkWyk", 137 | "jwApLGRfNo1VfM5vcx72hJrx6GE9cGtzc8RMdVBky9d", 138 | "5NXCFneRk6UumoB7aNyAX6qBRgUhZSH3aGsGF13Vcs4h", 139 | "4gfqFuTpJaZipnN6AtbqkuMPetmEr81z6DWJ7vATgYLv", 140 | "J1UAzB8qRokFafh9Edfx1c6ei167jPuunjwWArHgvJDE", 141 | "2kVD8bVdxbcyoLunEoYAhqk6Z2djpjJQwU2fa4X7GVnd", 142 | "88cU5ZLsAssZHatQ1W5rj1hhdkHguLVZQfgvFTt2e1jo", 143 | "D2zV1XGFRRdquyQJYkMxs8USN8PMaqvRKpfv8UNRnMSj", 144 | "Ao9pnNqoni4hZpCYoau7nsp6vqWbZcuoeiLUfB1DnGpB", 145 | "GDuPV3fMGR3QYZFmL3xixHMVqbCLwzkTxBo2upSvgWTY", 146 | "CFCW3PkjwSoAb4knJApK415ibXYuEqNPTqx61Dntg4qa", 147 | "2QpVRg9R7XmpScY78TfK1S35vyqWkzWK145CuoHdTaad", 148 | "BR165rrqL5bBqXskNgopjAnJcuswxaN3j1rj5waeDEag", 149 | "HWcpSAKBCRDNPYBRvKQU53a7P6oEr2SiQEK6JPK8JE7G", 150 | "51BkYuZjpuDj4arG6TRHutXn8DGkGsGThFrbRgbjVFQC", 151 | "A7pz7hiKVC4SCqHLW6MrF49soM133q5TDKvEMaXbsK16", 152 | "3nTmU8nasTDCYyyRKfRcSZ8GeHv8hgestfegGQidbzv4", 153 | "FAWMHpzQm1BdvkWKSM2QWmC9GAjQJcnbfo4pCHhetTxq", 154 | "4W8ortsiSp76euhGo8hNKcqPNm4TUM2nPWi4Lf4FsHPt", 155 | "C1zja7iunATUskTnnbPaYh5Gjk5UctyjW5Lu8Z5J4dwc", 156 | "BGu9Wc1Qffnzj4UYM2zLbZbfuPrh5yahYEaLerZpD4yX", 157 | "36YXnDVsmMsmUWDnKCFZySN5h8VsjFEH4E8YTeshjwYh", 158 | "8YEmFNSMtwgZYoVszdUPDiSUDwgrk6pQk6aDwtM9vWk8", 159 | "69Li7hdXJT3ardcqM2zCC3zzjkZ1aMpUyLevermdzgjn", 160 | "8Gz59vzZFjVGJWVxXqh4utaYJgT9uuZ2PSnzV9T5mjiW", 161 | "8qTTaTVRFzvc37vyKGL4GiRQWrnZz4CFBmNhYNrCoX7e", 162 | "Gxe8Uti5LHa2tiZj7DDJ96iqdztiCZcGFj1sVPypgBiq", 163 | "BnXR8iZbM5JxLwzL4gXox2Voy6WWL6jcmDhopJMX9Ym4", 164 | "DwXPPRmhN9p5KmqVJ8NAH1b8c3qsZK1p9ceTcyzDYQeT", 165 | "Hcvbv2BYLdXFBihFbZieH9eCN9QSf9MfhWVSmNVD3kcQ", 166 | "CsBFPvQwAoQJUSXx2Hat5fJc1Qmg7c2pVSrzu35oaqQ1", 167 | "AMrfgcxKuvdg5wGRv2WfrgWUpBGwMvtGSsovYTfPsxkS", 168 | "ECx6RP7a71HizgB4NceTGpMr1QWyma9DogPpJvaSFUPJ", 169 | "HbhE3VSLugjBVJntQsg7yiSe4B6i9hHNDZdUsVbecmWq", 170 | "AYjUczPEiSWhHQpB8YV7WEnpGaQqvwBNMPFAbnivgZnk", 171 | "6vmYECi4kXu4iCf8hYdYvAxrA9s1Bxq2Wh4jn5YTygdZ", 172 | "7gqgQ4URivsv7CJLJnEtvSneck3DgccohQg9gr4SNdyb", 173 | "Gw64LZdfSQpjtqKhUm9yBo7tGDR47DtCRUSbdsfzeWZx", 174 | "FiHZ8mj5VWEKX2upemqQEmTQygL7QyYy5W1Giu8zwNis", 175 | "ACMR4qEawUnW3UVkEsQCWMqfsuXr4eAcsyaxE8LMiTCV", 176 | "S6Ud9LWntZVgYCRNRHr9eu85kaLNg5qDdeQj2B5Y45n", 177 | "9MC1pYbcLHewyhTgd152He5mwqZWDGwbg57FpawTecFp", 178 | "J8SU4zkbdaSFqSn3A8x4vQwVcUHAhsYQrHJNv24RAhTX", 179 | "G8HuS5gDTmNVnLKCDz9Ndov7retLEWTYgrcCeSdKjnGM", 180 | "dUJiE7tZueLQoERaVfCUCuEbhnrHH2KQ84FA2nJ6d5Z", 181 | "6nFJkMWvuzBhYBUCDUHu6Mvh2kXEZvcKqgZytw4j9re4", 182 | "3uaXRy4AnW6FSC5rivXXdzMqiAzcsi6Ec38XxMgjUH57", 183 | "9222eCZvBRqjcow9BtZiHBVfcr7XuwhJ4PW43U7jnspg", 184 | "BSfHedkZJH53Zq5tvj5djyRs9Vz93uhPW3BKTCkbQCmu", 185 | "FHDaW8dyCvTNAofoznzET1hZFddsGLKVzsTWvKb1qkEL", 186 | "HBJtNkFbRunHkQ1RFSUyADMERVRVnRwYtWnZvUKTtcqi", 187 | "9t1DXBE2GyjMUwgUsr4h8TJ1fhyB9Wa2kX7Z113nWdWP", 188 | "5ba3RXHDFNfKtMpZeH1oapfTQ8QgpA8bL5d1dzeAEv84", 189 | "9SomttqE54jiKv55bve1826UAWqCawxTqLrP5hf8shS5", 190 | "Cbsbt7R6xqmk6ZieWhUCoUR4mD5WJYtKRgo6uynkLabB", 191 | "DKUVViUMeQyBGmpUaKB71wrKrHcwzUe12FFvCzmogsZ9", 192 | "EsUX5oCmXzKCgaGvx8WBPE4NqNawbhHJPS2Mt46Lrk4e", 193 | "3QB7ntm3pKEvnec9QV2Je3NcM38hQGSwSMpHJSM5zMw8", 194 | "3i9ujqDSN4Adm6WrsMn2RJB27xnQ75xMZvJxo6dgxwx4", 195 | "8pxfQRB7yBkzyj4fR9E66xZnQzQz95q18v7bF3puUKbS", 196 | "9528PAi9KFCQ6BtoofPy5AmC2XcRasaEL4kST2zFYBiB", 197 | "DkqQX3yQfp5dMKV3amon84zDZx41gvVpRJKd2FxccagT", 198 | "C8XnXaWNEpsDPHWS9nsiaQmk88RwhtrSbapii6QqeUyX", 199 | "AJXxRUaEkQZ8wXbVpJZHBmHs3TedGjdBHRd1js6NLgDo", 200 | "3gDumdKfsk17PgexkMt5aFYEZ8vbQ4u8YhKPEfdpao3A", 201 | "3KBdC5ZKB2ARKoWYGyhpoRo76qNAUiXVkzfkeTeGrz4a", 202 | "43Bo5Qbej8SoBJWye7JKAhNQQhKj1MusYYvM2sFF5Xb7", 203 | "993KcLfir1ufLEh2CDvgnPkMTmgcQpp2yVNiZGKfWty6", 204 | "DibAJdzKcNUCbxdytLJxivfTMF7FRTA7uSJo7BEARgMe", 205 | "AsU42de4XFVt8fHAzP1PV1k9UEw2PNkYCGkSo1QeVGQz", 206 | "Bpkf88J7jjr3AfPg2TAPWXEE4nGffUQMFE5uXjtSqUJE", 207 | "J3AGDYvneAZMSbM9CXEkRgEmcc1SyAoakAtqV8ieCgwc", 208 | "7eVnj8U6vGaovKa1bjJprtYe9UowKVwCXjhgKA999Fj2", 209 | "ELmRQ6Gk9rWioPXeee32JAFm9MnvLVLuoGRi1xMGvGVo", 210 | "37De6XPnbnWCEDo3z6QSD9VZ1f5crJZFEwSrjyTmCbcK", 211 | "AaUHF9wBZqdpdmRnFuNJksZRWFGp6FJRbYakrrvaFTFB", 212 | "ADvHRT9PDEtNxvZ5paT5iHqyR6DZ1qm35EoCx6ceuLia", 213 | "5PHt1E9dBx9BD6V9c7NTWCWB4niGZUUMVY1qNSvJbdeX", 214 | "8GPvppW7CoEypcS7TuNWNXCTRHZoF1Cp3PNuhCyJGist", 215 | "9tKyAKARZS8W9e62y9xYZ6RckWg72KTDMmevCrGNtHTK", 216 | "DGPnmL6hLVces2rhFWnDxdmmMMzFeLzk2HSTRuMHTgUH", 217 | "4x16jsWAu6zo5yMdLV3JYvWsgvw9cGQTHcbyDrFibDtf", 218 | "9DqnzViubafEeXYRbDtysGcGKogbMmx8wRF1Pao8UKX8", 219 | "H6vHWYSemMV1oR94UeKnbfFMsMCTqN2PNGp15EXUAhgv", 220 | "4aEQ9ji6UWtKwgeLDvXmuqSA3cixzw449iyNXwNKv8cb", 221 | "fxAuKPkXZBnMS2KNuTGwf5cFCGPC5gYiuPpRndiPrHX", 222 | "8Y9Dc6NYSGSYhLoiPNdokymNeprG2ghT85yNq4Lv3xh6", 223 | "4B9dEudc8XHd5CAyHT3rSG7oSXqywU2T3Ct3FPwFBzFS", 224 | "9Q8WgDa2KQj1cH86mY9eqkmdUu7LhgmhwyPawcHT1mLL", 225 | "3U5N681jSY4VLBaMLPxpwMdfFp8e7qDuRQKHDZREapzz", 226 | "CyC18DmiAa8DDcnRajHUCy4XXTAFt154HmqKb6zEQVYK", 227 | "da59NHLcZMTQjCxPX7TEBK31udTJmXxyXQ4AkGfHjzi", 228 | "BZeMq9duVwewTumfxEutGENW3opB6rCPKpjggzbixXKv", 229 | "6qohAwrMUZuHRauz6oiiFZ4qEwfrVuKbVF9txwy5U8QN", 230 | "EeLv2jwLrQa3XyM7S8NL9DoRrrdYxmQRYuJTwTudUSNN", 231 | "CVwFPWbzcR9VJBAMhyvw3CVhaujqbmH1CQ5gNErXUVjk", 232 | "98ZFGBAZt2kthsJ3m1rGsUFAfXiVj3oHkKPfT7JFoTG4", 233 | "GnfB7gYMYeqfaEtfsenTTrg14RD7AugtoA1R6BXpqsbi", 234 | "EcdD1oNwAwEet2E2nMkP34Ny2SukLL2SZnWDCLrpu3Kd", 235 | "PjRP4djyg7Q8NK1SpVaz8kx8rrqr7vdgifP6JvWaN4d", 236 | "BwRfAy61k19y4FT9NhFH2mJHycrxsPwUQWBqYN4FDtcT", 237 | "8VjaX977qAxq9ikGrsMV9NFyqxvUJubmtWuVce71qbA7", 238 | "CiEijwJrrFSkKukEZKCqYhFuehbXc1WYB6ABx3u23Fvc", 239 | "H8YSxCrfdKRFdXVc9tk362R4kbY1MnbkuWkgNgzKtHPx", 240 | "Ft37iG87N5yiCy8nZyC6V5ovQBubeYCM7PDBUTrkxcF9", 241 | "GKrNx1MgmJnv5rRePdyjoi32kQkPpbNkY6kzLcoiVSvG", 242 | "5Xt8ChmBrBAG9tVLSB1wWWrwbw68GH7maoBenAZHX7cU", 243 | "DiWVdyb3V9ZRsyB248ReFnG92sSNNmgAkuYnTrJnE4Uj", 244 | "EBXWa5yFbxCie7adVNB1fgfw12PvLBZYsHm9Cerzfqon", 245 | "RnkfhX4uoq2RVNPVW6PnJvtELCohVu86JdimFK65fif", 246 | "HVqf9DVB37uhQrsUEbf9zVPxPeVJMLZ6j87ifFtvG6km", 247 | "5L61DTh6XRmYseeHoD1CUhaiZjuP3Nz8QEzcnr8Zt17E", 248 | "DRaaaSANPvyRKFTi5qLcaHRfdQNUUdwubG2M3wftHpZU", 249 | "EAjfoUJQZb3byufApJbSCdTUJs32PszPsC5cJnsM2coD", 250 | "2enJGscYQJZAEanMBPrYWrvLYKj45BttbDhJrrsoRJmf", 251 | "EDNbSUkKZQ5jECLv44z2HtD4o1yYjTjQ9PpH8XWdPibc", 252 | "2Ut8Xxgwd9M7PaVJtwf25HfQbcVKE9LwpbVkXDdtreqX", 253 | "H5nwHTDjEkQY7DQaAJ3c3rDGeF2za43A5roTLmRpEmDX", 254 | "HRLrXwdZqn8k7d53i9Ujeijd3WtZkE8YPdQ7BK6LhDMH", 255 | "4qnQdhtgSpe3XYeGd4vVVAFF6bknDD6tM5DA3zEnrW32", 256 | "HDnVyPQ3juXJ2x3G1omHaPD46kekBcowY19QFz36Edf5", 257 | "6LEmedDFySW2mcaz4ZkzMwMboktsWJeaHMgATV8jx2Fv", 258 | "2VGTDyg6qNdJvVsauJ5BFRyajGpuih9UfG6C9X7qFJvQ", 259 | "79WqXzJa5GbmghvTPcB9GL8LLzqERJf9oKLooujmrkA3", 260 | "7mm4hxXa4YqaSaP7DMwNMLu86rJgXD3ZeNzjPX6ttwyJ", 261 | "9dZHZrGDvepihPBVG98XJ5GELAa3AaY6QmJ8grJTCZNd", 262 | "8wdeUHPZidaSZ9XEESUnJTe66pC4mPVnyKTGik89QTvC", 263 | "7dhSNBLfcb7RWFQy1KF2Z2WSU1EmA5HYvozzWZFwinK2", 264 | "5n5ukQgbUF43NM9MketNVdJ2wwGZ5U8SeVK4RAtqeuSd", 265 | "DGeUpbCTVrbuDYS8RPkKXYuBxyeLut4k65m2k8DxFpM1", 266 | "9S19JA9GrX361xivunjHYfQqmHjfgNxNqrMd8DDmboMY", 267 | "9gjhLA66npmU2W6b7EvCPZc4cWxNG1oeHDqEhGJAA5U2", 268 | "BUUjbuKv7cS48ZowLGKUyTiryLvpRK55co27LL33DNiJ", 269 | "5dAfnqDUbL32HybvSaWnGBwUKpDRmLiAsnCShSoShrMt", 270 | "QaQ3zJWZSc6eR1EDZWNBf9WVy4MSXTwBoSFRJ4p7jCz", 271 | "3teYxhVvvhMJYezcTF1R65CWTe1NfYZ2K16DX4Pdy3yL", 272 | "2Ci9NtVjtXQwmTCnun3vpvu35ZWW6LHH6gMHJ3bndH61", 273 | "JCn5JP1WaMdBxQuQPyx4DXuqoHnhcmzXmQtbCDXAvwP2", 274 | "8XM2zFtAx813XPQk4fkbSVQzMTfefkbcn7L7t9V1sfH6", 275 | "2QaCU9LaLCNk4Mk9j8NupFCSnNBKViNFedrhoPbAdu4J", 276 | "2fdMAWo7C7pBb1PSuaEQfNhgJdDUVH76wphno2TVQ9vw", 277 | "6ef2JAzCZKypnucULMyn6kVj2FBk9V54sWsTYJVkFyZ1", 278 | "C8q6b9HFpuBLXqNRgi12DPNhxXraVNkk5RUGmyHsKDaD", 279 | "DCKTz3ESmBbQpXXBbaN5ojQTWHnjwaDey6H8dQ7FZqN3", 280 | "7jskVadnji9TQgQAiErfoi1xCD9DNL8wiJLFJoXkgtx2", 281 | "3abxRnqCEvTGfdAZTi28yx6KAdCWizRa52zf1o5yJ3Uh", 282 | "BdFCfn7qLGUq6tZtx5V3p1UQqvA5vseWwL4nh2go8fmr", 283 | "FxpJgSPn9m9GFV5Lfkystpm2SmQ7uxUrhEfLBmLBV3kc", 284 | "J1JLJjYotfACqTQ8g1vWUW9BHRSkJ3NxTqJiukf3aTmn", 285 | "6vHiy5d356S7AQEt9ds5gYTWBCdYZqrkiU8pUPLwuqfg", 286 | "AXMgFqYZmVn1jPqLi586GekNDneu8jrUVPmu3CTmqegR", 287 | "6k936k3pqJFq9Hg8JkQmNezvMdXMUHMKM4Qy9igMNsZ9", 288 | "13uDeEqqiUATist7MCGtL62NwhtoWVkxUoBuEAmAxBNL", 289 | "BiPVYCGYHzvndLeGMcD2CEsstx8gZNBQP1w1SsZD5r9B", 290 | "GttcYvof9yofYRynfYqeCZsaEfes5TbXaf1EpB35DsD1", 291 | "EwNwtGNWWy3yZNxjTkwzU6DnjeTkewqPLrnjxUj5t8sA", 292 | "E5f6gi9MPgB94yUDCFaivCP6E9N8861fcjU4x9AFfWvx", 293 | "5EDxYV4N8cmQpEpvpxSkPAhcmMVS6pqnbvonr2pu1Fg8", 294 | "Amw4TDXmKCweS7JKwvtjrSKokPJyGiNd7YQtPPGUC2Vo", 295 | "GABaKajejhaRSMG45eLziBUb4k5SGPbf7NFPcQ7yej93", 296 | "9voDyhHDbDiMD7uQMEm4npYfFEhw1w6LqyEEZ1k2mGBt", 297 | "HG3UXciBzZfdyR7ug3sLCuywWK6iBNotZJ5z3VivYoes", 298 | "4D1J8puuVFwwtBKcbrymrHPaHnJaVY2csppduNrDzacK", 299 | "3eLqHxsZz7CXE6cXzpqhPkrzfrj8ruw9w4KYQQWWKM9R", 300 | "djGc4qUDzVXE5CVMe7XXmbDBCRFnb9Gu474AFn7iUFz", 301 | "7nPPapwrrUVTSaDWUaRSGpwoR8xcTD9vTVs8CG9g6CaQ", 302 | "CkjHLP5NSZfa7AhH5Dvx3RCmJddGtbmkxMZR8iYCUaq6", 303 | "CwBpdXKAdfaPGHcxhgVLC5uxfAtXUetz5rEchDPDyYWB", 304 | "6VibJHLA5aRaBMPejsB7kPjAKjUy4akcfFhQgRDRH51o", 305 | "7T3M28wH1uTvsiD3PwWAYcm91gdsrkCq2yECLqEQPvDW", 306 | "BRpKaqK2vWR2p2uCqAwthHzYPGrm3J8qSfqr65x3oKzv", 307 | "7VXnDqQXFaeHMDHXZ5CYtMqH4oR16JXYDbsj4wKasWFs", 308 | "3SW4GEuP5WDzGUR7X6tLABQLVRtW2si9hvK1rfntGide", 309 | "798aEiRBayAQSdXMafsT1acTSGz3yAZLXWJRvyVoXbs3", 310 | "GyBbM6cs4XadPdVsnT8fih3ZVsGmwkSkkyeZBXQjnPqi", 311 | "5azH7hNJ4w5uU2f1x4eMsCCBUUnKPA6c8dVqad6bpn8G", 312 | "FWqnEAgewj8QUyXizuqVZx8Ekb4UnQ7VBHcsjFUSDxUv", 313 | "A2ATiSoUrQRHuLXKRUDSuSSqBqdQBV6vpUiv4UQ1FpxW", 314 | "78bhrmpYDrLSSsPc3K43mjw66XBEPCK7qB59iCpNXujW", 315 | "FN2UQA3Txma1K35Z9pnZF3ZmBRc1RuzHkYsdUVJDHeaq", 316 | "9jTsukbtnde5fb6mwoRFm2jSNfr8e5D3yG8aVSCsYoBq", 317 | "2zTQpyAZfpMzPuqKfSPQP6SzYS9tqvHjSMTxiEDu9dZr", 318 | "FyzVikvS286aECyLhQMUF6dPH3LzyM3tmwDEkKtAU77z", 319 | "9qVgVjqbSPJKKi9rp5XzVsfkcrBJBvBXKbANapySANN", 320 | "Hp1n2v6npLUtbJUzzDNiGC3emQjjEfydFeXbF5o47Ngw", 321 | "34j4kmUZ7T7rwdj4YRXNKFvTpHZyzEu3HAPVMfVcfQmw", 322 | "6ZUpESKUVx3e26iUftddByhd4j7AX9MssNbynQgifcjK", 323 | "7VCragZ1u1Hh9dgRp7FEHJe4bFNVEzMdKdHQv27sSevJ", 324 | "AwHAU8nkpccLN7gvAERE3FKuRAZKPpxs77pLFoTgcGtq", 325 | "FqhUGRkYrNeKdsd8tbQUZaB3eFwkhDrUexoysqAPy96C", 326 | "HzY27seG8gGM3Qjujv33capcTgLPf9ai4ni1mVt7KKos", 327 | "89qddhTwByU1xCAsZMNQt9zFFK7Xw8257FjgD4Li4WJA", 328 | "FNEm7mYANZ9FkG3jDMGYUb9X1nG5bdHvGWbH9UFwf9tb", 329 | "E65w8ZYUAzyf9escRDtrotVSQazPyyssdcJQxShgdoYW", 330 | "H1zGYrkWATPTZ2b11c8EAmRpk8sqpxp7kg7ZsuHcNs9d", 331 | "AHHyPYRQqrWjt3WjFyuEvyTTcTNTuzThHPbMNeF6EDrM", 332 | "E4FQPZxc4aTTDX3pUmWYqvJ1kAkqVEBZ43fKz41j72V5", 333 | "Zu715Zbv35nQDftHVBY4xsKmiAWvsaenZmfCXZZw7Zi", 334 | "3NbJMJQmMMisKAPyHoVvgaqrBurQpSiuXmaDgWMT5E8N", 335 | "6LnMmMbmpcYd27ZtRbGu8pVYXWawaQtqdEcg8Wa7jgH9", 336 | "FH6J7U4oPsWrN1kzitpZKNWcPKBsUJujUagBjAHZjpk7", 337 | "5EUBWwy34KEWT3n4HgMf3yMvSbjnzzBE5442kzdV2Jpt", 338 | "3f2D9rWP5GYJbzPupf73ovgjLLna6kVcCQsK7vaghH4C", 339 | "EfTjMPtkNWj4ijvnRJCToM9wxWwbkvz6WYZF8MAS3ku8", 340 | "Ds5Rdii8ic75Dvfmh1rJcQgy2YpAGCg3ZT34Cho3sB4n", 341 | "3LHR34z3KQZ3PTLPgovQXFz4Tv2eRzv2rmZ6pEBDuyqD", 342 | "3ur5AdxJ8P6Sqe87gbie6Y68KmLurumdwRbepr8fNQF5", 343 | "C21vtoNcpsPKpRZf2Bmr9TTY3EgVTNVg6ZXyi9Bzfx44", 344 | "2nf7v9TajPsNmpd57PScUS8MKbB8DYTKCRRhKA71zuzo", 345 | "FkPpAn7so8dFyoqCGbyiLNSaLjW2exZmepNu3VsqVjSB", 346 | "EYf8vYKuUNWj7dRvmaaPSALemZGXLewfDPsHqLxAZqNo", 347 | "4XzRjnZwgSfYjmVbeBAQsX5vRBa8CszEGxZurtN6NCrA", 348 | "6ay7fxkj95fBJic2NRoYYrgsg9fBpa5FT91WDZKSSTKr", 349 | "2HjSVV181uzdNhRCBo1aprW3ySmxuziKq7enTVp92U4o", 350 | "2DHdghmKvYL5dcC8fG8HK4JRmNTQBzrfYnVMWWzbBd4A", 351 | "6piRCK6YAvZZRuzz6M3bRGsbSfqbLPPMtePDD2LbWsob", 352 | "ERSnVUA6GEDhJJxz9E9bs4ufWAp3hCon39jSizojjHt3", 353 | "4RtxbwC7yY1RTmz5pLSN6DX6rwT7qrmAA9xrpJcLw3c7", 354 | "B5GWqpZ5KpE5dxTU74MuKVrGmoDCFV8h8N8j3udgJHJX", 355 | "oA8UbyrjA7LrTojeKN2X5PraWGTGWjd7TRMKMN2H6i6", 356 | "4AHbtu7g8hq3ktyB2WoCthb2WxjuiTTU2r3EoQztmgtq", 357 | "JBFv7k5JUo1MeLwrzzVSRhkxT62R4dHiyfV7gHDbpMDi", 358 | "GnsfXxh71BtMGVZXb2WWURnJtum5o15DY8Yav4dbbvps", 359 | "9t7KESKvENjEJFuPyzzw4KsQAwYzibkL3gfaRNfKpVju", 360 | "8Qizh2seQKPGJBpUt2sBdmfh5KbeVem7J4iZ8pJdmqte", 361 | "m9qfMuv5pogmvADdVBWVGUT65wANRLz4iVkZqoWih2n", 362 | "7cQ9C5pEnnjVHQhJzY9KS4VsXRVtC9yUWooeNZ5ER7yQ", 363 | "4NWxZAkPBFPWZDbRs9PQRnkoNmRQZ2rbUQCFibGoqnCd", 364 | "5CrfugMpES7nagdc5BtjP9JSsdiJZ6RNRrJAUnxgxyeR", 365 | "5idDb4kGczwgCUUdNYxB2nKng6jzJPq6j5KSeDjeeqqA", 366 | "9VPsDtQkDF8Cfjmq4vnyRJe8cTP7MbkoHTj1q1bRyWvR", 367 | "5FrZZFWRABNbreBeeN4VWYn47iJ97st61KTeJBQSGLCn", 368 | "DZcGxfv7QVC3KRTNYkmUqX86zHMi4x4BEZBrsYuHHijr", 369 | "GbHeuFRZBnqbRVm8H9XepLqwedmySQNiKP8SQUrYQx6p", 370 | "2kLadz8og11WVcrpsLms5Ki2pct9NFyDpkDdRhppDs3X", 371 | "4NeWna2tUPQYNG8ZeCZZKNJV8971puhPPPbsn7jwKzL6", 372 | "FdUu285DjdEdVFhChtREvyP32kKof45EVwUuNV6mEnc1", 373 | "7q6bfVseNUa2eMRvuG4Bcww4HP1CpYCRy4amw39AmpZ1", 374 | "927gzBenReMK5RRyCTdAfUps5K8d5y3JPTHYqYecKdo1", 375 | "FhKoKXSxo9C2R6srbjm56vvnvXYcj4vJJn7JWVyac3qk", 376 | "4cNHWxKvqVxKxXYWh3Jtsqpq2SRU8XrvMw9HsatNfhxj", 377 | "8ZJ4cnFFRsw9ussMD9jUgtWLMXfh2X5QqWAXJ6rxm7Zu", 378 | "2yb4bcTboQPDNPa8jsGD7JbYFePveng5tKGkoAftymmB", 379 | "CggcoWsaK54BuhzyLNBpBSR9ePvz4KBwVAxN5HZwUXKa", 380 | "B4RD3ntq6xvW422r7Zu3DRNas2DHYGotYVP1dz4RGBQG", 381 | "FfoNWBFL3NXVZzmYnfF2AmpZx4x5dTCyhG7ewo48YkJY", 382 | "2ndHDFoqrUYswqnpTHax8Vr1jhqgFLeQKnVZiUBydmw5", 383 | "9AT7tYYPJruvh6Z9gMUkHrhRotumGWmfXVX7kFwXU7UY", 384 | "Fi5ekT1rbmf9XSEW8a2baPstJTCtAmqtZ2TzdG2LJghg", 385 | "5c5HZCJQgLfnRCKbJNA6QSx2qiizL2hitvg2hKJ87vtG", 386 | "FZ92xadLRtaHVtxwkb752dJoJmUwq3mVY8GzJjvA1nLQ", 387 | "FeF8iSRefqym99NX2fNX1Xa7sfEMBdy1tmsvaTAZcEv9", 388 | "DTZwCXXDQM5S4vsmSktPGGTdtEztieGypsykiKY1GVDs", 389 | "3XsxLxnwDsE8tU6DSnsNNXkYa16M9hqzi1x8gVSCGGwC", 390 | "DPgLCe96nu3jfMSLdH72xKZbFdTK3oRSXsZJqZLqGEVc", 391 | "Ff6EpsQHcozTJGvM419hTdpFTo1SoyLggnU15Y98RUDG", 392 | "3RsJ4vKSXvvzM2ofHEhikSx78JkHhmfNQvvxdHR9NuUF", 393 | "AFTNBXCzh58hdfXQKWf7GnvHe3YPPzmtUVKv27ps8XUK", 394 | "55DwGLfLDMbzSCiKqedyxNF1rJ4cZKr9fZMLW1zqCeVs", 395 | "79gjDD8NqEDnA1NUeronF7U6v6R6TRNkpbn1D33nGPzY", 396 | "G5kd3TP5RxKLuqUeuPMMkqBFzAnHzonW2aZ8sBF8Bv6R", 397 | "6CDbV6cF5Gf67P8v5Vq8LL2KqZyGWzHdQ3Anhd2d3ou1", 398 | "3Cph6iae7XMGKF4ucDet29GCUHX3iWUDEZJQPZCFL1GF", 399 | "FFzje5uqrMFuEJyhqFkbnuFF7ioZxV2de8YZEX2Spb99", 400 | "D3ZovYsuCsquHm69kMy1EiGFY9MKAtGaTcgAqYzQ5cRw", 401 | "C4oQFf1cCMak5jnRHzzudd1hDCXuQkfFEkhrEUXcvHBv", 402 | "CXJcN3vTbZUFUDeCE4BMqDTrh8WqLtF84mmj8keUJJCJ", 403 | "ESsJWs39yJJ4vJbUzc4P4yXeC6ocYXdxeyS4dEHu5om8", 404 | "5L49NN3VaXhpibCbMbXLLvwNKQFjh6kXUnvVXP3VkdUT", 405 | "CETWqfAgsXpV7Yv6cH6kqUURteKsWJQ3EyfuK65YAinv", 406 | "uQCdVMK4STTKRpDZDqaLMzZ3rWZDZtBGBZML4LFamnG", 407 | "F9Vz5PedwsTBbVbqm4vu3hv37M9LtjpVR4vLjZJrWhUL", 408 | "7CLL4Vi3aPNjzFSy9Mh7bqLrdEmna1DyuWsgGgUtNmdD", 409 | "MU8pcZCBFiTCyX9TTLKjKUEufCxQYH9sCqeniRUTQxW", 410 | "BbBuZEJmRnJGArEEPSUgFsub1PWPdbncLRbwpwHUBF8", 411 | "93bbBJRdaZBx8Gs7mhxw9uS1eHKTCsC7mqk6v8z3ZjgM", 412 | "Fjibnur7SjMPKiCpLnB5ZTdYHgsYnn9UAbXFWS4pw1kV", 413 | "F6fETL4tTNuoXPGCMxtqkmzh8M4iJghzYMUBtkhUvEXi", 414 | "3CsZLsUKGKZkbgR7BTfLmiELgqUPGL8efu3AuGd3oX9A", 415 | "FvAKnEYkm6egm1iYSPPGt7iYYkJUcQrHygsV1Zn64DK1", 416 | "BHHkDWLFbQkyzVXeEtd3zJmr5Dz54ZmQbYbH9BjXwDQx", 417 | "ETMKNPwRscEhuFDqDQaH48SQw7AMu1r5JWDc2pfvpfme", 418 | "7rL7mFxt2mUUKiihctinRf5Azr8kQBm9wNbGN1gmvHxt", 419 | "7QMYWFY3kwhEdptvipXpSst7eUcnGLXHrv2xh87dHVrw", 420 | "3DrU5DFTohr4BMQ4RX7m95YKQ3PEroX1CY3eqUwDRmPL", 421 | "6UNvTAcnebUotSPJWHf539qDRn8FjdKhtVSzgpDB92F7", 422 | "CabVBYMQQAdKZrZYp7HAQdPCRNULqkc7LaBkkHV2GpGC", 423 | "C1kSTaZLxjmeyU9xErGYs2cdQQjzp7aVghvQ8qQ8qFaW", 424 | "Gd9ZL59aX8j3vTaBhkcugn7ejJiXS8gb6rf2S4DDVPtv", 425 | "CSwQG34HEL8tnKKRNVxioAGfKKXYxUE9PuFTyUrRLp6p", 426 | "74Rsb7QndaXbJPsjU3BAemzWGmx8PMXPKNDeDBS4AfQr", 427 | "DoHUSway2Eug12qesdYEFbV9j31KDU58JvhBxzQkxxNK", 428 | "Af6AbNvgFmw7mHAXXLYL1bqBAnYZrr71y8PqGigUruBR", 429 | "EeeTGDkcqcJDew9RZvwVgSPc8mcZDUv7nkJbtUmtKtHR", 430 | "5ywCjkwQ7s7kxvy7W6venPbqEUHtc7zoTs8KkCLWxZ7b", 431 | "6hjpn7H29v9wb5NNHmSUtRp8BYhvFtL95X2HcmdmCnka", 432 | "4o2JUJ4C1M4TE79HbcjVVkDbyYJ7E4NZtyrczGVTfpJ7", 433 | "4RfTboYYt3eW6zVc8BXxhXhAqUXiPWkduX1GVaQkMQgJ", 434 | "6U8APDJB44WfGn7iNrRvaCmuYajniX1y5QwUJ5EUrpZH", 435 | "7k7NeFdwAy9zrECtzaTPbm4knHzJK63TWXR3p6LWq6g9", 436 | "C7CH5C88VWE495MafkeVbWxY2Wbcq4kLiHrFDkVAiKnd", 437 | "FBAs6rHgsaLwPFJHnjDHsjQzkG5Wmr79fpbPh97zyMDR", 438 | "3iXzrX1f83BtSXuC5fKiinjKEECJExPp1d4J9KFxNNSB", 439 | "xcMhENLmdCwrDfcTyBh4TeErGPGj1UBX2ypUJXJNp6G", 440 | "3XCzuJKRPvmZPHV34nLjm7Ro9nRvqFmUsm16ngn5RKPA", 441 | "5GKzbbNtTe56VckWWg1RXUMExBkEzRYxjgd9m8eAwWwu", 442 | "9CrYmCEXdSFzEwxBXEvGRGHurjAaxpqL6ZitdVgReFip", 443 | "6sE44G9PPVwaadiyqadr72EkL5Tm5tR3zS2yo6xwscza", 444 | "7M1Ky1qSAZfVwEWM2p2jXcWrqci4o4qPDWnWrtbadBKR", 445 | "4Nyph5o1mBp7ypRkymvLujuipqkcDf7HPDSGbQcxf1H1", 446 | "8SPWNR5JqWGEwTJGxgC3bshhuJ37EHzLk5iUZPWjmmJH", 447 | "7k7NPvgEngofjH8t5Ynz655pMiNzedgiUDuXKkHQ7CkL", 448 | "9kVx6thi7ZDHGAQthWnHh9hgSZTTkkdBhD5ishA7ES79", 449 | "5bNe3WWVi97UtUPSxpFMKZHt7pWks2rjChJf6WnpUHer", 450 | "3z9LaKxqnk2cAi327kGCtUCdBpnVjS6WinnnrBtKTXME", 451 | "HPqqppkuc3iJ3Lt42vNXZwwsvmNsNcDcC3KwtXnsHKoK", 452 | "HUHd9cdGUcUojH8BGzJq8XEeBBgARQ6VnKekZBAU2QF2", 453 | "EMngdYP38WPcpeSMtPCEywkW1uB4Q3UQ9kjgwNvFMQ71", 454 | "7Ygag7sLVf6oVNm514zKYq4VwBMKHvTvLDeHJsr3ywGx", 455 | "4fA5U4w4DfmNojhKKBCVHeAY5VQ4dYpx5uFQSsrknSFA", 456 | "Crfb5hofQaJrnLt55GXPSRVUR1mNer7WPFc5PPeCdbqT", 457 | "6DstaXjxoWZWRNcK8vnDGK11Z8FxiHyB97ysexibHoAa", 458 | "EVBeB8UQnUYjsvXuDajtJr7WQyutJKhUyWM2fRkv4KXv", 459 | "3T27Ro6Yhf4g24yjYdS2WBQrq9MarJJGuQLFdUCokrtw", 460 | "ECFXbZFxaBuVZJvU8YCMmKAwhw8usQuuTW3QfqXAdHix", 461 | "Ar7gcdWTuJoo5HFsmqykUXdWLuhBhMt3L769MgJVCKvy", 462 | "FjhGxv1VQc5sbBzor8tcFdQf5dY4cxKKGELea7odFWRK", 463 | "DqFhhwG9VTLitADUxJw8dbcCrcNPZkx54m2EQLzgGzdy", 464 | "83Zfe4QtUBkzNManjgumeHUAfNopJTSDpGEjquApynLw", 465 | "74QxMn8SsMy2EHrjgjpWJNzjaPGHRjUcLCJr5LbaQBBW", 466 | "Dje3dmTMbdRNCBnSZNuRz4EL6pt6E3X1cwSG9aL6m923", 467 | "GckjhSgQB6BmLfLkuN3tmFB13JuySJRALVmATFZhPPtd", 468 | "6fMbEzYHXJ64vnH3koStEpx8u5GH3vP1Ckzc1q8tN7Gu", 469 | "ACwcPsGZfSnNgXBQtLFbbdr9aYMbF2gdAbBQTR6LkcEK", 470 | "F2ziHrDgG5evkMZMUnYqE1UzLiebQLybxvocguHMVNhN", 471 | "Ft8W4hELHTCe6BXAXkHsLK3QGwWC7n7WcG8PqUqRDGJH", 472 | "G5PHWetwdjAMXQL8a4TakNyo3FRSvTMrx5oup8qw1dEq", 473 | "81Zz1gFMzi49rSHtMrpX1h4ACxRB89wdBs4T4VMmqi4H", 474 | "AB5325YqX2VCs9zu2AqvhfHi3CsmJ9NJPY2NEePAgFvs", 475 | "3FQZ91ExDrakf9eJmXdLhrxxwiLiWbJEkJVicjA48zD2", 476 | "8AbekneaMePQZRF6FrmWNwSZkeSa3eMyWbWNsdHHUPEz", 477 | "8mpjrybHrsXqdWK3Qr3Ukb9MgqLTomk7dwNMzt1op1f", 478 | "J7zQWPuJrN7DGFPYcVUM8meigkrbQsZTEHmtGPnD7x7Y", 479 | "AqeWebzTRK6mVqka8pyPbTdgqJH8DbUgnfFEGRam7Qm6", 480 | "Edd7Ay7DuNS3Q6WEVxVZA9uqzmGcncF6TbAop8aje1rJ", 481 | "8AMHzyK9HzkKhPZYY1hw5wS88PfezsTYUE4QThTcZZXN", 482 | "3k2RncVNuN4rcaVH11vAAWMf3Fyuh7KspVm8U8jJ9Lt9", 483 | "5PWYkNH2RDaN6eAQsxJRQZ6neii7L5Rmes7JkJK6YUeD", 484 | "ET1rcrnsFGahNS41raj1BT6PA2hVt52SuT4P6zknrvxU", 485 | "8rQwaAm9ZoWGfoNLtVtS6m78TJoZSkHEVM9X3sFLz6LP", 486 | "8SwHnSjfr6kLS4bQVsxSqhe42jqjmPQjxkCX3z7bN6mv", 487 | "Ai1FmgDTzBojnoGbDRUrj8D66Rd1pZfTjuwwEfuzzi9a", 488 | "55dRgEdGZaUnDKJpmFjUYxnaSYg7G7T6bq7tDRKffE6C", 489 | "3Y6NDZsAF7Kpg6XyYUeDqVdhQdLzCbdhPN76henGLZhg", 490 | "DC6ZDJfgmWeQ7Csst4cnuJDoNu2QpikLBcw36i2UVthZ", 491 | "9LXsrWfCTYwW6cwXqYUij59FzPZFwhWNBmhmLE2f4L5D", 492 | "EZtDnccM92hoBZtss8ha1yjoyWJShUZGkDqK2wHzR9PV", 493 | "2XgABnyCm7STwUDK2pVpBvL134RmDSuTbXjUenWB6ywE", 494 | "DQ1kwWFmhfGimSMu4t92WSrX8JPha7b5PPnAAm7ggCPC", 495 | "8vSdHQAdHL3zo8vsCX8WKq4BC8BHDjEMyvfW2i6hC3Bm", 496 | "C3sqJAnJcDoCpVcdwMogKrtQBtCpT9aBgV3Y4yKCpGpE", 497 | "ou5o8Wjk4Rs4WACxubwnQppGS8rh5zB9rVspPwmHEHz", 498 | "Euxh45DLwsp1F2c6RDykN1aCFZhhmcsaXKoX11GDCdve", 499 | "6VUU2znZ6RXUfC8Kbn6CkNFPQamaQ98iLiANtJEVcXDn", 500 | "HjhQimexRRmBCy4VLqJHXZtzG6VKsL2CFyWvDPWdBmC7", 501 | "8bJyUh1antGnWnwSEq4s23HDnu9brqoMiK8qEw3Wi5xK", 502 | "D2n14RcUa6ofo6eQoq4ucog8RSMNbpwpqR5YKpRCjMzd", 503 | "6MhqX93TyihQsZhrEtpd4ThNjsbzP8AGhmdfDRDJUbL8", 504 | "6xfiZu9TK4pUjHTfV7EuoQmLBLV7gz9wEFmexR99gDrC", 505 | "aEUAnWYGi66sFg5vvA2kdDpnwbzpdjA5XigqBJsR9XM", 506 | "8m9padRp11qvCZJPsDvsaB9xAGV98vuZubKkKEEBRa9m", 507 | "Bdxo16ftAMpjVfeERdkuWAWwHD2n1JB6o3AHFLQchvsG", 508 | "BKRAfzQaHFS1hxFskjSiJTbDpuMd5G82f7QMoy9TML6E", 509 | "52TTj655AF1T7wVJC9NRbb8u4EjjTCCRdeRTzDKspHmn", 510 | "2VHSwujaCqmZZkym7xuqdNKwHU7CNCFHrNKkSxk4wdms", 511 | "Dxm7ZLwtBz5UuqtyBraQBXrks5C4rNa8LfiW7FkAgiyL", 512 | "2qECakvatw5WcvEqEVsdKCTchdsBBd6ATcZXS7csZnaZ", 513 | "EnNhzvcdtFHwH5iQJXjQ47bCJyLcQom1tXaqGJ4NUNmH", 514 | "67nr9dHG7qvNMVZuw8LUywM4pHVuwt9QuXAU4xztVdpS", 515 | "2k57DA76FAoQnkPn95f6dNcaBbfhtg4XA85tHvn7E5yb", 516 | "7H9kwedKW8JAeD6gMVCUu9SioGSL4bNzuCDoQLdZJds3", 517 | "7xq4xuyZApF6rHL9cA2sRzi6Qr4opqmzNkHfUZjKoJDq", 518 | "AypUPeS3JzTrW4kEV6q9QSQ6f6QXiB76muCZTPBwYWRe", 519 | "9x3t3seoEEL8pq6xkPv9Tsi5RAV9h5GuWZLti4prU9qt", 520 | "AvuY8GmdbLwvoqHA2ZYF4S4DnkpdGAVgXpqbgYwfQWJT", 521 | "4PsG9ncjatpGirKb16MFc9JVamUPfk5V11FUY9dm4SWC", 522 | "2ntzTnMrVcaYWUX6aSbU2c2RkuZdhpf98rgC76xJVpC3", 523 | "6Zv5SnFiDPuEL7gNRAjZg9gK4oo9cMiL5qgf6QrWCkEy", 524 | "HGXWAvfUD3y4NLGN7yN5crQ2NJXgojYoe4XawM714btC", 525 | "FFRdBRkbc5MkdfxAwgrQdYpZG8s1JRn8iXkA2uNaTnsa", 526 | "8bGVYuZTGe4VnMsg9rSWXsKLa67oakosR8JTsk7XbJzw", 527 | "7oTutc6FQ18Y7UdNZY8nAKTqBaZq8vaeSdyiqqJY3itV", 528 | "B1EeyGdKhbNM18oTanSkWGU9hknuw2k8ZVVsY9FqXZiH", 529 | "3h6tVJKPhu5kT6Yvsnxdgi5K1gafRmxtEcBtQb5Cnift", 530 | "6JxsyzgQdtZaM9hRAjLecCUuvZAKgQ9XvCxwJTFuB6g7", 531 | "7rskQC2XykY8i1iGP4uri6kCgPJWKzx11HpC37wBYK3A", 532 | "Bp2QrHmeKJpHqocqEYqraYfiwC2h2jS3r3Bcqyxhsg3y", 533 | "kHQhuCxWNrKWtrMwe5tEeFVbeh3MHNN1LvEJ8mkJWDQ", 534 | "D3Rnff518G7y7kN6abef6qGBEPdtT95Ku4g4TBSmvKJ8", 535 | "7zsMQc7aeYMV9gV5KT9yfS8brGNAnvoLeAAggbnF2Xvs", 536 | "4PvSEb3hRgNMSbGTg3Vw3oxchAXVLmikcSjpQr4srenu", 537 | "2huMdASQAnDiNkwTe64uH6wtKCeFNxEq8RZsfToWu62o", 538 | "GCjomhuL6Sb1rdtwdY8W6SbG56P8M4goGADaB6PtCfqM", 539 | "EXsYN9vzzjbCo1EirorX1obBz2HYwV2DR5vXzJ7gfwbQ", 540 | "EJFx1RUZCT9UePWagSEdUU41DY3onBfiehqiWYXkVi2b", 541 | "E5yNcsFyrUEA2Xw1zpPC8BhP27fsdNKDwmbFNtJhVgj9", 542 | "9JFW9L6r8XwhY1MUFdN73aoL74Fyw2bw5X9d58eu7JfG", 543 | "AvGMjMxrkXBR2nQGcLi4tBULupriSDPHPwuZ5Y2WpBQq", 544 | "56M6jcLgENaeMLaag5qQs32Pkqkh6RrytfPCvQto4nv8", 545 | "pha6qQa5GatDxQBHi5AVpSu3LNQX4LHgcevtjCvFB5z", 546 | "Hfat7ft6hdVJJYrQUa7NkXuxNaNevj51yNDqJkGQTZN2", 547 | "6qAVqkPF6gMFGhifL5jKekMTDSTb5wDrN1JHqrX6UhRD", 548 | "8boomKaojGTo77iunQtjpYPZMsESGbJNvfwFEm1rugnZ", 549 | "FcUfHu674pjLsDunJUVyv3UGHcfZgzyeKki3CNGXFmdP", 550 | "36LjSxCqJBam1ZThU9vMABJ5dHQiJ767hqjhhNWBNGsk", 551 | "9hC8f54Jjhm7Tst15uv6VkQK3xTTBYmo1GyAnxBhKxzj", 552 | "G1cYKXMFhWPM2xat3uMZpvACcWH2zFy5nRyQsRQ6eNLv", 553 | "FQmqMV1CLmTudwx6z8HXW6pA2ifjjt9cYaT4TrRoHtm3", 554 | "2oT1vxqipUUr4aLZBbVphawoCZhWJMDC57PEkGSkjMGY", 555 | "AANoU8Mab1ErfSNVdWyaT9wADrGEY3gRgL2zdGME9UVF", 556 | "5qAZtxVTAPszsKbNpK4YuxkFieEp5nJ8SboL6To461a1", 557 | "FyLkJkF5zuEDUgXvS6dcm77NPzstfjFYiKPB4RvPrBEW", 558 | "Dbd8DPcvmDg4ppvu7N1hCQbaTCfLyqJiFAwPrJWQPUJn", 559 | "GRUNPpuqG1jdZgkJkmSu8znEr2oJdQqEZmmwQS6hZ897", 560 | "CUo3Mkd8kXi3PNqLSSCUF7XYs1eFLzSqs5iqQh1CtCks", 561 | "ASPyqeXDYReeHA5qj2xpFepGDAaYYppqTqejMibsk5VB", 562 | "DpZu9idfvRyA6mytS7qx3BtWM484TWoNkfXxbrJ8Gzxw", 563 | "6nzaFBmi5aTnAxfxMnpFdQru9NNQk9FUcmbWXmkoMeok", 564 | "6Nbbze4TvNaXNsjer4wE23rcUXBcp1XbuMSN89kWmSAV", 565 | "DECFSqHriQSqqd9WsH1k4HjjYHCJC9CPmAceByuD1y8X", 566 | "GMKJUSCYFs5Ew6NSkqAN6p1VRpme3vnqwBFuNGyKoU5M", 567 | "Fw84kPWieRf4sGE76NqLXz5VjLYNJHyRwnNNFzoeRJmm", 568 | "9Zf9bXJ5jQz7XEGUMS6VvyZVujKhnjB7Zy7JEf2fFPZv", 569 | "4yHbBoE4MxFNZq1ZgnpzudFhDGJoPgeWNF4zPg2MZ7S", 570 | "4zvbkqZt1Mw5nwZAqLdW5q9zYiK6rh3Dyoam7VnUdeZq", 571 | "7deSDFBprXT2Y92bYoGmQG8tE1UKNWREpj7Zh7KZ7JED", 572 | "Fbi4m5CBuDpEUjn7xdGoJTM9GnWUYytmPcCzmCqWFidD", 573 | "C66HXfm65Fvkz3xYySzPT1eEGZu1qpUpRJiYJDe5aXac", 574 | "9squ4YDoygA6U9QnQpJVpiEvu3bPNv5Wxskoi56gxp8q", 575 | "54pNuvoHxGGq9gbKkroobmZBGY7FCbgXNJSDczYVMtRm", 576 | "HtuhUAW4HGGZb6m7ybrAES8A3jXj1zCkNS5m6YenvPoc", 577 | "84NjMMy9f7xQpDyGhuhWCf9rBUcJXPjLD71i5VxFWfci", 578 | "F9bD7MYuaLPofabaxVbaod9NBDFovYJMfUSwDRZ3crcH", 579 | "BzaJXbFbx32uHNFtyNUvJfb6UskWAL3tbpTRh3pNennE", 580 | "HUWB6oALrWKhRMSwA8RKiPGwNgBSaj3E6RXuZM9DU6Vr", 581 | "FBQe1EzvSxAdREBkmrKq8fg2ETBAQ9sMwx2HUg4HL8Nx", 582 | "HCt4cAg17odNsZoYy4HULeyqKU21LorLTvkFNGVGrZ8J", 583 | "9VaCT1tbRa88ThkPqciTckqumQAtNQMyw94TtxAgMD3", 584 | "GQ5JkSqWXv7UTc2VufMS3EJGQiPqkVS3Nd2JaWAczG9H", 585 | "6x65WqeWqVatPrcs6uvWgAoFuwfTFtAK7T81eBhnnF3P", 586 | "7zvyimbABT1czNCvh6r1FNNeTKPoRDo7kUZkhcBU636g", 587 | "EGyCmuogCYL7yByZ3rJoCGpGqucs7REJuKoJ6EJHDQ4T", 588 | "2Dn7gmQoqPG45bHHZKjkZeHyuYC4Xm8bX8eHAMKj445z", 589 | "98rzpXPhK2utmNBgHxvGbSAMRn4RMoHVJfK6opLxRvCt", 590 | "3AW63xLDZUQqzQVsz6vQaF1HHAp25oQm9cn4jACh13TC", 591 | "2iTBWeHxBxMqdHpcKujXcfF6G7mjtUUSjzop2qWAPdMN", 592 | "FGpDR5qPURw8xp4aQSHzE3CnKCYNKovHKmhEeJvhivWK", 593 | "H2PJ7uFHPg7n7LnybWQ3ikLhjYqMgEHXf22UzmfVWCxB", 594 | "4cc5svLcu1xyYCGcfwDop64ZMs6WJiCY6JrKF711GDLu", 595 | "GJGvva8G3EggDznuKDPJMoCjiJnDcyRQp4uDj7WvauxK", 596 | "5D7nos9JVugk3hJyYBavBHJ9hQ4QpEmiYkXtDrDEPKEK", 597 | "FQ42JgQqjq9ZXCc5m6sdPKpE7W3Rvh1LEwhq1TVN7q3X", 598 | "2bQEwTTrLKDPxBRDeSpvgMYX9HSfxS2w8Uj3dvzZYxSy", 599 | "Ct5nqPxEDACnWyjQxT1pWuygoMb4KzZFJoFY8oH4dpEU", 600 | "4rFXKVXqAB3p1hnreLD4frV9gVuTLKXjbLjB8guxXz3a", 601 | "DTwd6eBgnnYc92yC7NpmBPQN2CVffQGLJheLvGurJ41N", 602 | "J8AGt6jmg2ABM2mzUKc18TvYXNhazzrqZgYmuKHR5Bew", 603 | "2eRyPFHM4cXqXHLk4PaNnP4zk5t5TzkXmYYjpaMhL4QH", 604 | "2U8DoQw7YxyjxxSZLkfGNHmtmTGA7KTXMTDy4nd54oty", 605 | "7KMFtAz92hZRnVPUyfUqUvVSixDD1NEnc9dMDM2VhVjR", 606 | "GrubnUzGaWTtyy96u4TPTcNmCU4weXKKiKVjmwsjuoQD", 607 | "8Qx4rSLJUVzyhmDoV3f3nrqcQKzrVXXuBbvz5K3gb6Av", 608 | "8mVuioyrmatiEo1PrwTRWUhn5jp1fHKA1xwMSejC9S1d", 609 | "5AngYXkt5BsWbG8nZAkg34tUPJfGTtZhwzWx5agU34a5", 610 | "DXBqmJUrZadFPm6ddnKRJaUE1GmCGyAQu2fUJTvuLfLp", 611 | "DYkuPXLB9AC4KA43tV7xb9M7HngZFjJyDXpcTg2Vo8Cy", 612 | "658dqk6FqVaKaiT9qW97zg4jbMiLteqZwRGwnFmTpt2g", 613 | "9vnEQ3ah28eeco4mzHzmGGUT3SpHeouvDEvfbYPfamzJ", 614 | "6v8pZy4b34BDsRY47nqrSmtqbDp7xz9ABRgCA4R2p2u1", 615 | "BBHVBAbnm12u6fiD2hz2i3zCThEwiKL2wi5Tm9z4bBV9", 616 | "CRLAXmrjUAW1FossHU7gCyXW3GsQMnaGy84ELU8KCazU", 617 | "6YjKQwxq3HegSiBGVNxxaJbaJZYZXA2GU1LP96KnkQWF", 618 | "6mx15nYAfTDagraanVL4QfsFT3pCkXGUFvuq4UZiaP62", 619 | "DJax75YFFcVdfyL8gVAF5vq4aLvoiT9w8Ud51gSq6RDf", 620 | "ELRJNTiCkPfw2YJ8vU4isM8mS5YXcwFcLyxy5ApeHRnE", 621 | "8LZJf1BzA8nseBYJSUw1EjiyPfK1omJXx8x1SgcN1Xxs", 622 | "3nj7cdakstw3mXyh44sL1E1pz3BX5oZfmfgdqHyXmYJx", 623 | "BqnN2VK2itvCnYsrC7hFrkhh84ZfWXzsUVZ9mAFqjBM7", 624 | "HL6SARiKyowMSGNCf65VvaYJWjDMazeuuFUXRALk8Hz2", 625 | "57PtdPv86bE92CdGGRZfJ2MKq5Rjbdeufz1k85H75Wg4", 626 | "GY6SBnuqo6djHXL8SLtwZmssNXWtnDc1DMqXpw3xLmmw", 627 | "CFRhuy78CM43kusUYnJHkgrgiUyMi7XVxAi6Zxb1ixk4", 628 | "44t3UxBn9Ra5DQS3H4Bcz8eE7pdw7Cm3RXBDC57Z4DjH", 629 | "EMbafQqsRyY25ZbG7ELMQuEucLLocKjWtbwMWUhuySfF", 630 | "FWTJUfS1JF9yQN5BVaYyqYPEU7qieVsMQCrzmo5DEH2y", 631 | "GWNKeeiutNu6d75yBmM9Vwrd2MK7MinXV87AGPB6hkzN", 632 | "GzbJ3tK3oTrc5Y9NFpxzpQXwGZDnsinfjZQqQXxgFkct", 633 | "6fNPhKLNk14d9DdgwBC8NEsxQFKHFJWJ8yzS8ZaaLVHq", 634 | "4JmZNjwzmm26eDQ2KUN1ix8DknpdwnhSmUeFtiQtdWdv", 635 | "H2JDRs5br1jGwcAufqj4jKQXktbgUMP6hhELFZfxWSPT", 636 | "4ZujEuN3r9B7KU1xYWxVGjWZbgSqWsV2obeKEhcZFv4Z", 637 | "4iKnL1CTvA3yrcchB17vz3yBSmaj2aLvhxPhtBEXvtdd", 638 | "F25Qtgo8p5hq9PoGLcwXu4MV9mvB77cwc7pNo3siLZMX", 639 | "3Exvd9XNTrAof2L9YjKN4a4NDQsABgPhkN3T3QDuX2Bb", 640 | "5XicZSv3QBG9w2VnQBdNZbsHZpy1ADNp7oh44N1pzR22", 641 | "3u76mnybE945RAC67uohUf2xhLrrwXoAz2uxCnZ3Qouo", 642 | "7bwWNdnfxqhcQx3UwpyS3Q81d38BmWYK6KRb5PzfM7uy", 643 | "FPeNhNbYawE4dKBwCxieTLv2X7izTJzTt95WBgpQUJSn", 644 | "H4cHdBqTTSu8CP5nozrgDX9jVDUhHTiNhVNegSijd2ug", 645 | "5AryQygJdvJjQWiyQTpM1g8H4XJw1aPR3UYnoskXzFov", 646 | "26o4GJWqxpJPMyREPhVJng8Fp1D1qiW5PdkwzixMBqQz", 647 | "4zbxHTYrmDrp9ZBT1aK8dm2jGKDz6YoERgZsoR3SwxHF", 648 | "GkeworrebC7Gjp3gjqkC2ZmRD2SxTgYxeYFitMYzYZgN", 649 | "FdiPaJVaYPc6W8jCoau7hKVXrg5gbbVePKQ6WyEkJkpV", 650 | "sDKwidbTmcxwZmc5CHSLGnqBiFMNJvwt39X847euJQ5", 651 | "7hLvv5q8rN78qPfhiuavkVD79kdbMx6bdSa1me77S9py", 652 | "DK6vi6YJduez17yHAG26MytgNZJxcAzAoh37D2H6S9Hb", 653 | "HLALtrtFTkD12ySdDB1yxu47oE2N1UTp2LKDZShWSAww", 654 | "BDf5Ktu36kTnEye2wRZjxQ2FSCbXGFLVJutubnma3Uso", 655 | "97DN2GchnVpBgSpcThp2CZ6vfs7VZ72GBpozu7eGZmDq", 656 | "4y7ezHQp8gKcEVDHoEiHj3AiVZA3my1yULoWxYuZkEsj", 657 | "F9G9XwzpdK9ZfLyCAgwYUXXxPi76qQj8b1mAsKJc2s9L", 658 | "HyHkstgtFt6gcswoU1F7hoo82ijDpcc88ASUcgz9Bgj6", 659 | "97K1UboSBBEUBffsnTvCb6TNw5GenPCXkT96HkemW7AC", 660 | "8mag2L9W5aNUQjsMeKMroBwGWoJTtiizXkraKxkkcAFo", 661 | "3irxCs1FTnMz6o4mRQ22WMJ77QqVagaieYvMMR2oK2cM", 662 | "87jxspA1kYuuPHk4Fy2MugPjWdErzGM7ym5XpUHYNXeG", 663 | "CZYdUw923L3KEjuMjbGQirjw7zgvrFGsUHDKp4qy5B31", 664 | "EyXVgXya6pPNtKxy3sD6w3zwoBCYiMnJ7nuMwoLvaWe7", 665 | "CDHx1zuaUdaMD2H1dhtyiokAY3z1Ec99uiiq9kXuGDxB", 666 | "4cJ4zC6pPkdr4Awb1K6ywZ41FB5cYux9Cv2J2xankiud", 667 | "s7pEZ2VrHahejxmU2s6kr7otnF8tp4GMM2f4KMwA3Zr", 668 | "6Ryf7sBA7kZ7Tmya9F4wYyvViATceCEoLif2bTGc8WcP", 669 | "3e1HRMifzsV2DbHpYtzkoRF2X55MeU2ztvzgEKTy6j2o", 670 | "FQFgs1FF4H21k6zvX5pcE13ygSbwqFe1GDebv7hJEhye", 671 | "73uCRLiAhtv4otV3DSyZkPHWPz3LuYFJMohK1rRJkJtA", 672 | "FjiBgyjVcJsj5t9jNBvq3pqUkfKmcigPKzDDibTBkHRq", 673 | "6BoWZudMUxNZTiQJqqVoSzkaK1rNuGbxUZp7uBasivpW", 674 | "3xBqJJ9ojU9SjwUv876WsRchMS8KYSsHVkoWR1aFZ7ch", 675 | "HqWKJ2Dch6ToUsccxNNiHreHKySnZpc1w2agktRHTqw", 676 | "HJBHC9jzkDAWjjbN8QnoWprLweVgz2dnwhGWWTJ5dzEG", 677 | "7eohEyi7yNDYgsaj6d23TqfAy1NX9Q6hqGmwRMEPtFB8", 678 | "9qXkqduMvyJgLmHo5LA1SGr8fxKLfM4SYBDk1u6cQig7", 679 | "GD16iwodXWCF6oWvNsbWW5xAt68jDaHWVgpBwF4NehUK", 680 | "CwZbB2MKAnvC115LqwA23P4gL27DBjXtb7Fdw3JPUdJL", 681 | "G5nBXzWabhMhBVLg3dVLPGcotp5mAYjRP9FRyjJsw9aF", 682 | "9qWTtHscH6H9YWQFBbCzR76ynfx5MLApWKni8dNujCs7", 683 | "pqHzGpsH2r2r9U1NEoJGyCgKGS9UW69V29x5j5eNvAo", 684 | "3nvv2tjiLNMhgZtiviFUGSFXNSvWTKCSALSrsGHxp8L9", 685 | "7EEku9WhgTjJ2RNkcNgZApTmrqEn7vzRUU2ghmiYRBmf", 686 | "HyUdLjRyMyPMHmTshjAXQdT2YhKHqCQisq9c5wsCaTsU", 687 | "4qcd9NaEBMbWAyC2NP1zdv1pM4p99dos4sh616uaPCK9", 688 | "6UmY5cxiv6eG1mWNG2QMu4pwWhe2AjvggoNym7ApyLX7", 689 | "5fQiCdw1pPq5RCyyhVdpvAGWx7bBAjBtBseW6ZNMWDe5", 690 | "eBQZ2ao5CnP1GsNnZvPytTezb38Jgogn9aWnzJnSzT3", 691 | "3yz4TUjrWUN8AGmtXPrtzCYYMjnGh94uH9nRHi5oceJp", 692 | "EVhCiHP7UU6DFSZ7vwgJYovbpXya36a2cxD5GxWNf9gN", 693 | "DYpt13WE69eGi52WnzfFiURcpVGy8Nz1SLVQcVff81Kz", 694 | "G4NyipQTxyGbHH6axF8xjuE8WBU2AjLMq94WNwPZrVeJ", 695 | "7Hqtnyf59jUrcaBkajC3JEStoRMiy9kPfwWtsjqKPVNH", 696 | "2byc5Vcss7mpppA7b2RugXDCa8thNEporFAnbsJ9T1v9", 697 | "DozAmH6wyYPDxeqnZ8QFxXi8vq8FuDBuCQFQNKwAeHdt", 698 | "yNUdcjf73QUHAqgyWwpmqwuk7jxpaxhRw3tN5xVZT1x", 699 | "72gTBLYrTYBJz3SnaLeAP5fRnGdvUyxhRFvwEVhMkfL6", 700 | "N7EJZxEZVyFgyy51hgwKKXs67rW8bNskEaNCrLyf6jy", 701 | "Z5d2on1RZu55SMu2BpS6L3G9cjMdXfxR83mtaqWZ7T5", 702 | "57tbrgmShDYF3Mzb8YkmZDPHymXx764cvqvqbucgSTud", 703 | "GHP7qdDGrbusYh5TZPMAEDgeAHai6YK6wnXmeSToFeX5", 704 | "CCo3jg91fZAJpzNtowR8sRFqeNdVqaahipwWtt9HFSgY", 705 | "2MFSHjnHRZDCukm7X8dPhttnT5LvbXdpb5iCGX9fpd1f", 706 | "2EAdPZGnEniKTLaPZL35NJiY8wRZAxCeaGsoWP99QgNr", 707 | "Cy1EG4fo7MAPHuqn5FHBckozQv8BpJQQqMNwjhq6DZsH", 708 | "CbW132EDnwjKXGpW9aTkmPdvrEt8SbqxYvUM1tovBss7", 709 | "EHop839rkZ8p2ELSFRqvU7rvHH6tQf26Y2j3xL5Dq2So", 710 | "BjFDeHsxm2D75QXcgduyYG1YE1TmmmZfbqfLRiQbjvcn", 711 | "H6ERpzKamEuKZbCVEqd3AxaagHbJLB4Koy44ShCQZzzi", 712 | "FuiuPqEbg22vf7bvhxXzmhD2dQxRRqHSGRib7FpXhgLi", 713 | "3uU7rUwc24jXsPdUzf87uYVuCHQdskefBfZQ7nbeWVin", 714 | "DKEDmFvD9vpNqmx9GayYS3ytETXbFBCBdsDNTpHLeTc6", 715 | "BUjc71La1hw6xViiXPX8whwQdEkJN84PQVkDC31Vdj1S", 716 | "A3F3duEdrLZGX3NgQAUmjw9ps5qJriQW6FNzr9RF8hkD", 717 | "14dZFJaxnmnbF6dEZFnZuYUv3Vuc2JK8nMa4oY4c7PTy", 718 | "4HjL1UBAisrZ54PGoUEQa1tQoyWQFhQgPDT8FF3TUfc7", 719 | "GWm9TwRrqSYodbWtHN9U7aLek2zYLtvp4uxuCUeRkNKf", 720 | "HHGH2A3ckYu4enpiwKpmTYtYcerdUHyQ8pJVGZGeDhUZ", 721 | "5vrKfBz9nu1HLRwcCjGHb3svWwapNMAnra6an6BvXCxr", 722 | "31Rtsvinm44ocBmHCTt2agLCsDfJXXGsjT8q7fRFpkU2", 723 | "7wMJDc3RLu4M3gvdWr1F5aCG923qq4LqVPDkbG7yCUjT", 724 | "6SZhSRHMCJZquNzPxRhSn7cPDQf1k4vVQHAUQENuUoQ7", 725 | "3Qv1No5JSTmFndmudSLtfS3xPJwtJsKYPqsQwoSnHKVF", 726 | "6puCee7TaweMBnS41kk3qrgyrbjhmbGGPV33WKrzcBBa", 727 | "CokDVjQTsUqHciossPSHr1HbdYotZSJVd59Hs3zva3ST", 728 | "DMQe5wmcWAwsaWDtxRCdpdpRWuVSeSQ1hsPzxLdvyGMm", 729 | "J5SJ73e5pW9RJmC4TLTEkcLj32FhJViWfmwz3RQpDzpz", 730 | "ERzwCU1vn2v1pUgZrhnGTkjtCD7ACA9h2VxgXjw8i2ZW", 731 | "7bqpyWf5QcUgurisKdWzku6n79YucHhswqYHvCzSsif8", 732 | "2hHgpx2vCFRzCbHTqiTuYmdxQ18SU8yXGSMy5XBak14G", 733 | "9JDEv4CDxtZyfRzDgYFsVwp9u2CjAwwettZQw7Xju4gn", 734 | "BeUDcgptcuAdiRmgmKeVmy8YSSoa7qmwfiTfvM4ZSekg", 735 | "C5V4MCq64Aaw8uPjcA3yoLdfsziLomLgR99kKQbuRD8A", 736 | "EdqokoigivJravM1pCDKugDSVbLuQAXNwK42r9kDnDBJ", 737 | "GA1fWSjkUkpPrY2KKTmefnabpkxkbJDY5sETBYwfUped", 738 | "2tnjBpB37mwSAp36LzxRAuCum4N8EeHaYgTPJExjVpWY", 739 | "7RSURiAikLnP5p8n49BCAKi9wrfh2iF3W9g4BWBMmjbw", 740 | "8CSx7ABWECuDbKuxBy94iFyqwFpUxPv56RaSW9SccLLw", 741 | "AZ1NknjrB6qzKNox6tZuDRXqPWttDzigGpzTjPnHXzp8", 742 | "7ARYNudthH6t3J73bVbn3q6z9K3tpnBCTSytZMY8PvsV", 743 | "6h19HYPHEY56J29NaDVMux4v77AC9hfZxu5N5WHazWaH", 744 | "FUgrqGzmnKjCxCCpF5UrCVpveMbMrMDcnA2MRy8uZLL8", 745 | "99eM1HgPsLc8sonV8RbCCF1qFTXMkf9cgqDZSa7HKRKd", 746 | "6Xbztv1aSA5uW2fKRXTUUQ2zhNDNQ5a2k6e2XAJT1wop", 747 | "6gVbEjrsa5qEiXNZQx23tVNiNop4do9dWVkDXfbcScti", 748 | "BSEuBnGwFLqGUWiDLAfM8h7m4qseNvVECNjM26wJ4Gd4", 749 | "J9jFsUSaBWEX7vnQet15eSUuebTcTeQtZoHG3P9XW7wK", 750 | "SnikT8mAcBxqvsQAVXhoCXK2itGwUfvjGF8PDno8cne", 751 | "D89fSrNv9mzyewSiozDr9NmanDQyNC7mDCfxU3mt4rKY", 752 | "G4D6bMHPEyspTW9qe53Kpbks94wf5RjfEtsQA4n1qZtF", 753 | "Gz7YB7abbF4RhEcBBUGSs77TfGK5xCWR61LtybZxtVdU", 754 | "4rGnf5XeKS5Kv2WWo6BbWatsXk4PLfmxuZ3rQRqattgq", 755 | "CM7xGAw8gdUT3135EB85hw6jdHj7mHwrCewRPgn8jSao", 756 | "EuiTsxKAp6RnNFXFV44ttJ74LtS5ZzjwAGirSnvxpeKD", 757 | "FuZdQwFcVyoXvBDVr1KriPJPEpPSSh6f9qcZp69sG2Ss", 758 | "5nY15qvqc78aFvP4VUKmR8E8sBEJhvHadj2METGVZmHs", 759 | "7W7cQV2E48kpj7iDJhFPK38Zxp4XjcuTzDPmheuaR5uk", 760 | "5Qp2iFbziuFScTwLs9CBhkS9Dz9UBsMGWkbppJKDj26H", 761 | "yPkzNRMWudpDg6oLeXSKcbBC8iaNF7YL6fpsQ1NcWWz", 762 | "EpoUtH3mARkdhK8sNbhWYsfSuKeW6yvV5mr7kPy1xq4q", 763 | "CsBQRE3tmmeH7iStZcVxBiWZCrveg9NGw5JgfQfWCz66", 764 | "3GrpncHvE2e83gx8iyXgfUfhr99j5pcknsrpAXAfGHxh", 765 | "C8TiiZtriWBNDRfj1y7ShHnxJEJdkqstanqSDPWJMe3H", 766 | "6KSsRNhRDJoANoUJ7JXT7B7A63f3SFLzxeftjKnLw3rb", 767 | "7piVNnVRWuCh9RUUXEPZqpAyMefhBWyPuyhPNZX4oLsY", 768 | "FPL3LHvZfuD2mP2TN3pYYRTMgt2v9Ls85y3WxEDDEomB", 769 | "CUsZvETF98fwR7AzHp7Mr7Bkus5szEAAJtLT1r4w1gpP", 770 | "7sCWf1wvGNXgUfCzWxeAgigwpr31rVgvX8HCitbgCzzT", 771 | "DsdEH7JkQJR3wGRmBSQ4sfEJyn9VrotQqyuENhCPykJJ", 772 | "GC2agt8FZnE3dtzNCZfq7G5WKZDysJ8J379JEFpPpi8R", 773 | "BM2LVZTWkCLjmGLUhpUHsZbAAZwkc6L9YrJZquJMB2gE", 774 | "FJJ5wUJ4296F8ZJDudwDwmeNgAxGr6rcQVN1QtXFKQwC", 775 | "4HsfgyxaRQuLQC1RHUDSFaghbJ3oReokpUXtcwjxnuSm", 776 | "BohBiExq5jkQVStPYHqdER7TAowqNRWze5vQH7gbM5sr", 777 | "9eKUcJ746Nq7b3eypGdwuTK4YQZ9wdUAqKfR9i6mFDvB", 778 | "2mQxUgdBzWbbwaHSNuY3kPYVq7ukKZtewoU8zSZHe5k9", 779 | "HyxfCor8wLZWs1zTFcmkxh548a8mxvimKQZPj3wYbh9t", 780 | "8RWtiKdZiKV3VJJkSWQdkt4Uv2sULpjhns6ybQ4p1gMH", 781 | "2t3GEx7hKeHLuinxuTypRuMhsVE7TGxyPLwVfnGqcFXG", 782 | "A9hLvq4Podn15UuUwvbCULFCdY2EAKA9Zctz91LKH5e2", 783 | "131vLroJRhL9Tu4HN3AR8LP8k4qkAXSSiECPsry1FFTC", 784 | "4SFra6S9JSF4UUL61wSHbW9PvenW5n3EEj3JWwjUo8vC", 785 | "DLPqbfBWkq2sieGoFDe9fUR5qG3gixkcHpgs5nh4a22o", 786 | "8WSKvWgYziCXDy4Ndcx6WRbM8NZJCVPfBu7fnZ6L4YyG", 787 | "ArAqa91qSEYnFvtGqVYwwW1gUrncQgeEqfFdmYT5496T", 788 | "HM2n3UDQcrinZcUiyAT6BLohFTsELi8d9V6gzWYeJPkY", 789 | "5ARkcetCCfEspR66STioQXq9N2CsTwJpwESPz8WnNC87", 790 | "FVDEimZFokDwVS1ayDJfLu33WjXyGGguSi9XnWUTRvkZ", 791 | "4HpY6faBgE5k6DwAq9sZpHbFdBjG8Zm7doWswHXJWoB7", 792 | "GYd2jeM6mBrzT2enMPJGSV3n7ywpKLTyCvVoxvSnLD9P", 793 | "BS6FmxrS8JGZSNZMPDB8tvJ34gooJ7Yu7wBEzfSACZaa", 794 | "E9oXHCvbYAc1MCzEXjwZ5Ez5tsvSUeJw4v8FrC8TkuSh", 795 | "FZL22V68rqbZr8nN442xTzRczCj6ViSftubv1JYAPmsM", 796 | "HKYcBP7JTs9oY6xWBQYRq56YJbhsZEPmdcpNF7L4qqWq", 797 | "993wx7A2Qnq7hPxwnuzgnw3cMQPgJYSbwxjckuhix55V", 798 | "8kcSjqwdCL2frXbdGdWDWhXVDDxcqgq48hZuz5Ko6EG2", 799 | "6BRK6TJJ2w8XZvUSB3VnJSLFSUCgJwoVs1s8ZxVC8t46", 800 | "76q16g8PM6CfTZSKf2Svrf74jf4fJzkCcchY8BggUF4u", 801 | "8cnadbU4kyF5NWHwSpzvQz7TXZfp2ee4xsbjtB3B2xH7", 802 | "Hkf2fW6F2SpRCcZ1M7dwURwErSC7oeAxfXfebgfy14VL", 803 | "3gkXKjwtdGSMAt6CAFUKGGkzNo8V5faWFvsRnEX2vs51", 804 | "2XskDxTArs9uHSzA74CKVR8nemRFX78DwUs8wnrVAdAV", 805 | "7W1AQMgSqFNMcTbx2z9qev5Q386Lu87C5HrRwZd2cZKT", 806 | "HSoREe2vFo5wAPwTynRVEfAAxQzsxNVJEVTbjKyT6QmC", 807 | "GcFyebY3vmfb9k9EEzM98TR676FhFZcpwkhXrZUHf52Q", 808 | "8bSoheajc1towDE21u8wL8ZocojDraUjGrAngcZqzpbH", 809 | "GcZ2DGUb6Kvf2NNYvEH23rDSGMScB7sxFeRzW24cz9FK", 810 | "6h3qT9HqiCfjsi54TPfxzASWHYSTVNjkJPkWGst4tkRU", 811 | "FA9Jn7UB1SMXCAvJBmviyjBvPaSZsxkBiR7H5QJgwN2G", 812 | "12Tb9YfkgS9YJFFeao3abZ7L3apqXg6fVgVCu8yspRCd", 813 | "6aze9vy4tvRztF4xkDr2zb9eQW8YvkZcm77dytfSbGP9", 814 | "7GBQgHZBqBREQHHyyR4GCExhbA6fuwaktdMaZJNTVbWU", 815 | "396MjMeEMpeH32KthsmbSr2fePhd1YxFnj6uRBdNrhze", 816 | "F9WmWrjTnoxLmmDPWkcshHcPenda1w6oeyyYMZFgpiB3", 817 | "37pziPzjBKsHFSfLyvdzTGd6DPnu6ZTN8HJ9qJMiinaA", 818 | "3jdSXGYT24Anj7rtydi9AEaXvx3eDXkXq7CVpbgfkDRc", 819 | "FKdJSxWCFpbb6yE1wTa7QbMiNuoE8BEG8wNCuE57eWYE", 820 | "3jFmz6mWu5Pp3yYaMDK7kxizB69WTJ1iNCh1DQ1UohC2", 821 | "Dhnmgaj3RhbnssttEcgmq1ngSVY6Y1LDzLeMqfRbBitw", 822 | "D8BojgeLyJMJNtzFVD9BvNiYcM2Nk9p5xDRtroSEaJa6", 823 | "DquVmLv8PnGzK8DSp5m8HAM4PubSKeF5tRmFVt1835XX", 824 | "6ib9pFto7yhXAc6QayeqvxfEpkBs3YahcC2S5tnj3nRD", 825 | "GsicAvpabLhMoGFT4pAMuUFuNXY8iZxTcF8j8mtBeeJU", 826 | "EmSXS8dS6T3MZZR3GqSP9egsgxw8LenhWS47jZN3a3g", 827 | "HmtYMay1LvNgeibbFLw5PNnuQdugrjfMYs7via8Wy37b", 828 | "4UNyom9HXk5xek4PupJEHNFCEdzN2HKNPh78f1AZmFST", 829 | "8DbEgdV3wpEuMoKSeHfjUkHoZBjGQRK2F9v5MUnFQv7R", 830 | "ChUCeiNLyZgAtD8d8oPf4m7NZqcq38YaCzcoTHhgMX6y", 831 | "CRoKCEDVzqCsZ7XFj45Du75aFnbgTH6S1rPzxZSf6njt", 832 | "REtEEErQ29u3czVB3Ux29g9KdfM2e12af7cLYmcjqKx", 833 | "DCvyH3PDU3pEZDX2hifffcXyMER261ouxvPZfbyS3yhj", 834 | "GdThkGx3a8PicQ8NqJUX4WJoG7FTBLxaZ8EVJbWqhmUa", 835 | "5GCeCnZQ8AQYWUZNwxdP5qJj3NMmFUbqQkie3HNmGNuK", 836 | "H59LzVkAen8zug2fJ5GuEG3n2qGBai9fagNxMf2aWt4p", 837 | "744MJ8Wm1QTPVwBUja4Lwym7hqnVFHK3AFAhBHXrrdMj", 838 | "37ytFUXpCidaU5mXoVLyzzxzHfaP9myjySv981ofrJYV", 839 | "9y5ZSLaZcLYBv8uoWuYtatfq5GtJwNTncTZUm7nUfcTA", 840 | "JEGAAahzmPdBQmvTQqxiEAkHLrNrm6GUXNWb9ZTmPUNx", 841 | "9YHZ4mTSrumaMGHzXz6pg2t9AWa9gU5cFrPLs83dTguc", 842 | "Cxbdcv9DF3Kkg29ya2kmCUEfT5WKdSFtY1MPLgAwfmUJ", 843 | "EgL9hFzUre5kF1tYFso3yeX5gGRaS8pz1BHCbPT2DKHx", 844 | "4bwty3M9nnyQvU8xpx3kgXoWdie3zPWVfWpw2M3MdvyG", 845 | "5REEtwnsCS4pye8a1N8UQPMvNxTmBYXRrbWVhGnKEQxy", 846 | "4RUBz6bZFGeXqizZ3TM1cSraD9jQuHcuPL7J6jfmao88", 847 | "E4xTS6hNNWLEdzUbP88nk9N3QMVeMbsAHkR5FrWnhp2A", 848 | "A6YZJGkHpSP124Nu8hxDRhMvQdNKDABqPJRGL7HSyv2b", 849 | "EPvc2yLrYLhZHzY8JFeDq5HxbKkxyw1Zgn8yBLKU3huf", 850 | "9vYtvNPg6j2bZQiEWmhNJu2engh4gxQAkyRzKpPK6iNF", 851 | "Ak7PRGocQAVgy9xkv17MYXz4dmHAjdxsk5QKpRyHrNfM", 852 | "9yivQgy5kjjnCo5nAKeXK2LybEj4H897885tUJJBR3JZ", 853 | "DvNWLYYqiTWa1rgykphyqkiM6xBAcy8pVqoggFRzYoH8", 854 | "BYALnvNqLyVsNJ1NDVsSHU8HxSuQmUattXZbsfiYcMbm", 855 | "9R4dGEW4bdLNHmTtJ1UHUn2q9L66LkCpZpUSArQAc9aT", 856 | "HWcLoKJGUQcKrL8mGtfG11eHSURvWeJbmoNMkw7dMYSD", 857 | "J25GVyUib8yrJpWmGqPeTDERg8gkRxnFsWgzr2GipNxs", 858 | "7jwXeEAw5t63vFLccxpbGJjZSWdRwBd45cbCKmwSdf8F", 859 | "CaepdZrVXGVyNFKN5Htt9M2apKwoYGbLFLz7vDhEecWZ", 860 | "CePqJFcss8EQp549xNNGScAJPcudRQwvVLet9nevzSn2", 861 | "26uXfNpw1VsEmETiGHoK3ZqBrtMosA9yhVFW58SX4sU4", 862 | "C5SmqXLyLR5XS9HcKSi5m79mzPRGdWqZvsm55DDQTGvp", 863 | "7gib2Poww8VGpGP2A2uCcmhHHgnAoWv1pKBnbJeFA9Tw", 864 | "DNtMnttZkeUjr9b8W6Qobm8uJenoWTiu6Ces5HvrTNcJ", 865 | "FmGYtseaknTr4qdgJL6iiAvXbGwgN2LY8XmAuGy9m8jy", 866 | "F9oR1hX7ZYEQVhz9hGJZeXRbKQskFxMMLrbt9JcXUp71", 867 | "CZuAx8Chou9R1Btb99m2BNDGxdMeAMWgi9UiqgEtdDhD", 868 | "Yqv8ppg7qHiZP5JT9pF5EcTw2hRKFot3pvZeaFEA4Af", 869 | "DbAKi12juA3SZtVXqfPThJkvacikifiSgvwS95cRgouD", 870 | "DdVeevjbkUNCArdmqLGkiXeg9mZNXw454dW2Hau5gk6N", 871 | "9hLa8EU7FXAoUcypvDXDuSQ9riNbnfg3ktZScLx6DvR8", 872 | "Dkg6YD4jUtfaJzJejQcdD7HGTmxdKpxWBMCCgn5GGep9", 873 | "GtVA3aryAzEL8m7cTkVCC74WSkcAjzJoLfgVyrHfi2D2", 874 | "2qLaZoCxyWoACtsK2WqxAqX6KksxPbH2SQz2manMDicw", 875 | "7fmjG1KpdJGHY2NJLVtSbtCZFG2NR59rs6Az3WTyyR2r", 876 | "3xb7vUjxYmLJyugZHNgHy5x63xPYADH9dwbeqqPQuS95", 877 | "3ZmXdrY49NHyiVpALhCND2rgPxKYt1KTYNrWck6iXwZu", 878 | "7ZtY42qdD8d7xFBt6xsa1kcGYCLKT8WTxrsCC4Kky1RQ", 879 | "6HwFAvsp8sAALVpidJKxyVskUV2ahxtWnutFvancBgSK", 880 | "55MozMKEgaaUDYV3vmD5E6BS5SCBNCJZ76gjeeYrNVVS", 881 | "3w73Rw16qfPF2jMs4PV6WtqwiptQ6Uh7Y964q5xSvXe8", 882 | "B7xeJ1JmbXS8Gzms8sP1Fpz36yNfZ9SZ2oEzPrSR7mmw", 883 | "4pWCimAx1p1iSYivvpdsfM6JSz3DfR3yrVHJV2t3ZpCK", 884 | "5euLewBjRvEX37awZgWz2URaV2KBUQMAuUCKjVM4td8z", 885 | "CUWMyhZA1HqrRGtbxqedoQLfWkJA79MBjV6ybZZSqsTR", 886 | "5B8f71SWMkpt2QXhZAhgbmK5PRsWszcHYaAzy48oM5eb", 887 | "Exar1biJt3aPzmDATbnPESPSyL77ASiRNaDxcncVu7Br", 888 | "5AVw3e42mNf9Lky6HbgMPs8kSGQB84ZeNQt5sdH6sKwd", 889 | "HqwYRac6Jcy3f4C1StCWycSfWKmqB3Y7N92s1PhndzNF", 890 | "5nFhXLRxvo7iJKRfSxi7BAF7unqHAdoMaQkrTEfCdBya", 891 | "3LXQArJ7Ywawy4h9ZxtEfDoo1GpB4xtgBXJknWn3L9ER", 892 | "8rf1uk4xw2j9crP2SUXw7kXTwSKfEnbh6tEQ7SDBX69v", 893 | "Cj7UtTaLs8cxjwoZnMHRGcNLUfFPXFuLxTioh7PGf9qw", 894 | "DiVR9aqwGaVvXRwDh912kb6YfokQKXeqYiN8KBTt3frM", 895 | "9FKzyaiMwpMseyMrCCwQ5RSA8bcu2VoNVQtxbfz5URdU", 896 | "GVK6Fm1VDdGxhW1b1ckBMtishuid3i5GQJBC8bnLG7Xn", 897 | "Wp6joL98ERjcNhJTxn6oJkXzc4gX61ZMEBEhZjmRMtT", 898 | "Fd7yZEQTd1U7AahKrgebeLnfwpLDDYLvqGiva5EoQkm1", 899 | "EzVoGTpAS9kEhSEEq2agVwoPVXr5AHwjjJPPXkbE6zzc", 900 | "6sFSUQBPepP8Jvfc4ixv4NQxQTF2T99ZtKkMN2UNJGqh", 901 | "C9XpKhRB9LRMYctN9brXfgJo79Ny5zw63aBEtyb3RNsH", 902 | "8Ly6m3kuG1b7CUsMdu169xTZ95wm64DN8YTwAY35bqs", 903 | "CMYYdBPTsn9yDSjhwC5invoS6osWJQvzZGF64tZzLEEq", 904 | "44K6aA8m6WeqJPNqTnQtKJoyms6oGZ4Eu8si2fLSzweC", 905 | "BKEEuV8LcXscSnQrM4NECn9KTgeZyJf9P3YCPAgPcxjf", 906 | "FEhFLgfLrfVYrApUnH8UPGsDHwvPhUTuVM4jDKimWr3e", 907 | "3RFVBLSUBRRpjaNzJGBSxCgPeoHFRAtwnTDza8Wqbrrt", 908 | "4GqrGcY4NA2y8mtNA6DeWQo9oaXjG5hAcEcAFVWpkcxs", 909 | "Cw6tadCnoBTBZ3CDdAMb1ef5wyFcT9B2iSRx2oEPLTfu", 910 | "HyHzfRtv4dhTsQf5FqLEPdSfMjzjdNp6SwNmZjSWUnEo", 911 | "Gsf7fJbrnnrLFnWXSuMSzUdKSkGJVYc1qQxLJqNcVvpM", 912 | "BE9U9yKrMYvEYU1Z77TeU13avuLZC1baVrvaRC6LB1aN", 913 | "FPzWUv1TGraWccofyrDLKcGT6XchngfsFSSgWWHZBrft", 914 | "9nQEAaVWg5cVczY3aJvRxHNEGv4nTniVqRgiSBNW4W6C", 915 | "GoupjLrb1mKq9JN1kA6WwjV4rz2x6zJvMHnoq4GbwFXg", 916 | "ANNQJB9goUQrbM1xjG3EJLMM69uh4NyUj3FCUvjqYPKa", 917 | "3LsEDnfLTsdFoZvq3erkdGJrCQ376Kj1keGGqCsjFQqc", 918 | "7FpGm6KHmYoDQSZUiWxJfo7JtiDYQ8pnTLZ23cgPAihh", 919 | "A48gh3knSqe3B29TQNCD486wcdTxcrMH8q5gSwqmKEkd", 920 | "DDyLvGGGhxQcAwL6yDdoutsQ3SHWV8KM3fdFUjrheQE2", 921 | "Dtbu6Fwd6SvnzrqDicXrznjFKeJLzjW6eDPamafu6sFc", 922 | "F3PVhGSm5icyNS9EkFmfEvJdU4bFduFsvHtZwoHwwEGW", 923 | "CNDjqjAfXjBAcwGVjhopeFym6Mies7xjQCPvAVHabaio", 924 | "5Lceb91UjXBSQvaGrxMvdeJ9tbAkUGuFAdhiPqMN1NtE", 925 | "ApjfpExSgnQeM1Lwh2TaGwHXUoken6BUeBYWKpXNLW2V", 926 | "seNLxaoQTBkkBYJDZxzpLydKvGhT4Nz1m5Pv4d8JhSJ", 927 | "58a3NCFthkudbgZWaKnwrgh9vcPXAV5S6qdyft4MDs3C", 928 | "6CqbsyWhZA3CDDx1S5NhuQFDLG4oraMRvQRuQLD53GdY", 929 | "5UuTXJP3USKuY6Vn4Q8UtvbT7xFVgL9XAnjszVS1bM3y", 930 | "G7xJURoP2qiGSFBS1vMJ5nBbWuPpXLoBNewad66t9kwi", 931 | "13e2PhpEo3w3c5jfeN6ACDaBvDaGtqhvVUWvhYk5x3Vy", 932 | "Hwf4e9jzvTwV5RP7V9rkrcAtJGTccT2W9G3CWAg45foY", 933 | "BqdV2m1LNcN4kG57rrrNX3KAKwTftMJpgxZk8FdLbTbV", 934 | "4xENt7pX3AaYkGyfWYrtT7GiwzSANq37yY5xk5FQLGAs", 935 | "FXnVo9yopD6s6s7pYREasqzR1To2zgQf31vk6NQnWbk4", 936 | "4LVFYnyDjP57on5BGkCaDfpgDxCGjMzeythnjjwmd1Vb", 937 | "3Crfr1eKtLowaXrp6BrrTuhfuqT6SFUzKXUbzMzpZScZ", 938 | "5TsNksSngv2Huu3YxE1UqAd1ZYuB7yMqPjvhiyh87TBp", 939 | "6ZnNVad4EvgcWrZj1DytsYj229XnAEAwrdgu4L3APJRE", 940 | "2xLxx2VJKmYcqyByViHR5TaSPLAKrNHVU31BgPhHhZfn", 941 | "59AxQ9v8hLsnDPo1yVxoHytPQuZRUspmE4iW8soj5dbe", 942 | "2M7fpeV3dFrxmR6UP1Vw5kvbQgkidGK8W73BpWeunXmk", 943 | "AR8DWhvdEVZHD3TCDLbVGKurMAwegFKCsZN7rkz4MXGB", 944 | "5toq3QvA9Anh3c1mfByYyMNT7SgopZ6v2FobemKEn2BV", 945 | "B85c3P98KEsZqYe4EyyD5t97ukaJapp2ZddZWmcGnjSw", 946 | "6z5p6czSkWFrMG1Ppzj4cEHRXditpiJ3UzKQkyLLPENT", 947 | "BMjnFmVgvy7QJL6dYQuYpg6bAAUSboBNwMVkgHTfhmSv", 948 | "5goBndGmdGMEwpkh6ERN3BcUNTJnTwiik1f2RvUtq5qW", 949 | "69p1y3QoLi8ZqVGwg947FgUCYVamSSXDpDAp3JE4JNPX", 950 | "3LDZi4xscsUYnijY7f3heUKBDFbpX5oMyVw9E9ggfues", 951 | "Hf4xUBGAc1DFwKnYthWhRFAMKAH1xSHEyDSKKYLCGmui", 952 | "43tjm54HERankdnDvZ46NENWiBRN6bosMJUXd45G9M92", 953 | "J3FZN4EV1MjhUyLE9uZYV9HtC46w6pKUbd6PxzAtvfCG", 954 | "8TxFM2BSbfDf81CXb8FZSW3Pxp5YBHpwTphAbG8HQDu1", 955 | "7yy7V7ULXXyg5WqBiz5vFywyVkiEZcpqeFPkwmXDfKaZ", 956 | "CA9gBgBxAak3WThbc3LAoj7Ws1NWWKZfbNa8jbApUqFW", 957 | "9T6GxtYf3nddUNY817MaJNu8x65qyngkzNdnVop3GbGN", 958 | "G25P5mwikk46y8yhf6gWe9ApSTocbSvvwr9UyprtD8Ef", 959 | "24XMppeEisstN4WqrVUGbCepChm8TWML9zP7hXJSaA6t", 960 | "qcC7W2BiZnuLEktsVtzWAaypWaV51NcGcsVyvL1c7of", 961 | "3Yia8s6UsFyfgBjn5YZDezNnbAN6kbxYEQNnSQiBx5Eq", 962 | "AmZB1NYE6yTDiW5GJqFZUrhojXn64bSMgQi6s4sWLyXt", 963 | "6YqsZjJZomN7RnfKQ1hEVKM25Mg9g6BZ7d2PP4akPJAT", 964 | "2kJse22jhKxVshabdEmnBbREYLZhVaphoJMrjsGYpSdC", 965 | "97mRBZit1ZQsSr2nBCndh2MDBRwpkWczibffvFc9jb7M", 966 | "8wxpn9p3bNy4ZLg3fBEAkcMKDUdp9QbHqGuvvbut6LPm", 967 | "5U9juBnbH9m4zVGwBufq9v8t9zaVDMdnw2QyAu2cjf1C", 968 | "4ewrJwWDK3cqonfYVHomRLQ3ktfd5Expzb7mn19EYunt", 969 | "FtRZCUf8b6awSbj1AMUNvRJ4FJpJfscw9K5FXRYWmXGL", 970 | "5UyV6L137FAXaFPKQ6RirCEmQgFVDWD4TcGcxd3jbDo5", 971 | "GtfEgtCs2LGuea4J46wJ6RNCUexU2EMdiVPFBQTq3rPQ", 972 | "6eJKpjovGoFjK3m1h5EtaSczKHzPWFUezKTdo82uw4su", 973 | "4mDTvntiYkuzdk7YWhaWn5RzuufrNqnGHGdguzmE78u4", 974 | "5G3nH2ymNC3VU28ftLN5k3cfqn2PUMDygJyW7wXJhcmp", 975 | "ELYeWiSMshL75YLWMAHiWFKckBZ89iM2UcdeWE7u324r", 976 | "akmi2TK1tcRWaybM4xvp8cD18ZStiN2BYLRy7prUhLQ", 977 | "Afw5aqq6oaGBFBjyz1FNxEB8hEFR3Z6L8M44n9CnCjXw", 978 | "5vZJjyXCQryFERanMqRr1HAHtzQMfxMwQQTvCqNA3iUg", 979 | "AQXmF7oxrLUSoBpmETCsMuRNCKbUM9okWJ6W65w93FrS", 980 | "FFo7hx2uXfpqW8R2P3K8uPSVou8BWf6puj6KS9S8REKa", 981 | "Tpt8vrGxTE2dyMzEv2BTBzanbMg2Gnpszyg44k5dVhX", 982 | "EYvZFspAMmwihvLHe5QpBDhhMRf6jHHrFgvSNhUKTGaM", 983 | "7vggXz1TSxAqjXXRCTfZ1dCR7CFdF2NbGto94kYsE8Bj", 984 | "C97c6vr6QMJARBdCiBRpyqp4Mor3SERThhgfCEDG2zRA", 985 | "7LBnYDeXBVtcyHB9zKfSTVjWL4h6ANbTBr5w9kRYcrm9", 986 | "AwmUFufZF2cpPRghVVnxMLibk2ssqvmx2BhZX61ijwTs", 987 | "HJHWHeySZiE9i2prqvnapCVcM56aTyNM3BQvfUouNBKE", 988 | "CLrYHddcQ3XUTVQYM4cpodT1Sp4g2zHEcuD9J1RKjUDp", 989 | "FVcNZQozpRTpBhWbRFWUiegkmF4G3ntqXKHTBjEFs7sq", 990 | "HiiELkPi2t9gRdsYUnqmu5mDnYLpG5RDTFNLW5ikWSYK", 991 | "3388R5SUDPmPgCQkQmCX3kybYErzAq2nterAqyGJBz9S", 992 | "6zqEdcfsDJPna9P5mS7j5NVJsc5mjzpzgSQxinWaEhb8", 993 | "BQi2ycPU49GzNtEMtEt95K98qQvkV4Wc43az9p71oiHJ", 994 | "HBvA5S4bkFeNTpJkiCdnhnwoTHweHoWusaoHtgxoAkJ5", 995 | "3ntW5Erex8r3jEY4duGmZK3siq4AtsJ5BRGSfd8fBoWn", 996 | "CAE83RF5gw7JA89WVZUVVxNF7DENDiYVtea4A74SRSbo", 997 | "EYTahizeKgBgVtCP4NExHqSuCuD9ahGtdtWcnmMJadSN", 998 | "B2jWzLvWqRnAZatqkzVtsQZLobQXsLK4wf5Mfo3ykkRd", 999 | "3Ej7dyVRbjWCskw3ZerkiYk2Qg485LFUKfB6g4csLY7s", 1000 | "7ea1Dk1WCSLf26rSXk3bkMKczQm73542XKmm6hfVi7y4", 1001 | "3m6Azy6rLqYttBULEGsrNuKc2BffwiKHsKUmEDBQmwXJ", 1002 | "9qCUUakuTH3J7gS4nR3gtxZwXVUAwbrxHhoVCyZzKnh4", 1003 | "7p11tpTfsiXc58UJ3gqKes2Tfpc5cFsiKEXRRZ4oGkTK", 1004 | "ErFCSREiRGhXyE9ETgZzabCznxCkEYQ5F4j7x2Gh7wfT", 1005 | "EZKLSL2B2jc2TgPUvxJkWHQaKLRc1LZNdNStTTrgjsan", 1006 | "APnp6soUXYFLiGQTKjhbwWHQttfdv2VsJ2L4KBUnwuNz", 1007 | "AiT5BGftE9TESDW2aBG2WcFYop3jd14QUi3n7gigZVNm", 1008 | "2cRqVNdJSQPCPF4cBTeDdFdktTUXWsKYYP5iquydQeSd", 1009 | "J7LFoHPQ3ik5zr2UCMBnosPhn1Gtp6HYkNDuZ7RnaVMn", 1010 | "9C8rmX7Ys8oBLmedeQDf1czDXT3MhZ6CWJW42Cqu4Mqx", 1011 | "42UZCoLXU7dVutXgZueNjtJbb8FZYufAGsRLGU9TcMxF", 1012 | "D4EAstqMXcZZhdc4iEgaVFnrZL6QwhrX7CVhaNBWgw1D", 1013 | "UixxMR4aJ2F1SWMsJJVzuGwKeSob65AAidqDGDw7VYu", 1014 | "4QEa4Byg36auHBdBeEEtB8jhUFTvwwL5YvKzk5ETs4zF", 1015 | "ESuN314bXcGQHNfNmZ5Lfr4hxyugwNzQo422bLLxUdry", 1016 | "DSrU39cfp7Z19Gwg1MFon3jSk7DxJ5wRaFm49gjbDPdZ", 1017 | "3BE3fxWQPtC6FNuhr9BiRbJb1Yh9Y3MkqFJPbT8vnUjs", 1018 | "5gjB9AgUVRnZS6msuAkepzceMyUW6aBLLJrsPSssCnhw", 1019 | "8peGtBF3Y9DWZsifwNQXEEDxRd5qJF766i75BrDj8Vjz", 1020 | "C6dt7x4dyHZcCKSqqommPrpaz3QZHQ1M4XPcQAnuY4oH", 1021 | "DTmrpfgeidXUePvrn7jwMWGfS4GJq3m4px4xsd7cCe5q", 1022 | "5GWosACoqGXEYbWcpBa2EUM59qKn3dsq99CbS1hvVDmp", 1023 | "DuSXsZ5jrf5oPF5G11MVeTdKjgeRecUypiizwA2qo8HL", 1024 | "39EozGRmZKBntMmZW42HShZjNEqDbTmjp28KQcv9nnGu", 1025 | "Fkg1mMwxKHKNCuua5fX8EwkM8yjy1ANLVLCZ5fycLxRg", 1026 | "FMcHKv23DX9trt75mmM7yH12HK9QshdX4yNA3fhhGWxF", 1027 | "EmXn6BD3QNNJ2nXsUTkVpv27cF9ukzaCpXG8uq5ezaUL", 1028 | "FUz86XGjB79Mp1kUnn9o6xPWL3s5ad4cai9gzYGs5zJr", 1029 | "2TD3URXYatYYgve6oaRtoVjaH7Pz34bPxvi6WFkXwa3P", 1030 | "4t3VgEbq2vfyR2SaNGSBbyt3Bd6LsnswHvdkQ33b8Rgw", 1031 | "5xCASdm85pGnNXyuEe7bg5ZD2rgF3Cab4cD4TCgBsPwS", 1032 | "4qcaVhsmfDjFrpPzQcVnG1L3tjZXEKXUn7Jp2JDue46H", 1033 | "6G8FjFnqqjEiQTyCZ1s5M5myCoa7BoMngbhf7dBJwaAu", 1034 | "2N1CiNHP8BorjF2UdmJjVSxuSQtrjDcrJT7BebSLD4XU", 1035 | "8ViQr3cSrjHbf9CqNQWt8FaRB38FTGboemEEwKo8ow6d", 1036 | "8V2VGuMa4P78u1Wje6ERSCSzwBpGU6RJ1ZkqGKQ55r7P", 1037 | "EHbo5MLrHBFiyRFkN9QsCvDLUs68S95FQwGwzUDoSrjs", 1038 | "3C4jUfmUN45pcgMSXk8oxLogqGwyGbd1xrx4nZ5DW1DW", 1039 | "7NimqMZTKJKQniqCKXfSkRaBXLj2WmvJf2EtciHABqtZ", 1040 | "2hF49oaHX7S4w9ouQ64mxqBEo3efEJmtJaQUmp8X81xS", 1041 | "CCsmSYs3YKhVHu8oEbzNCYbXSs5DhBNQPniw8CWTPGR", 1042 | "2e1HnYaoz2RpoWznLQpTaCjHBQx8yZm54TXKhBkmerQB", 1043 | "6Nv8S9fAw6aZRMwq79TLDnMhBWNGoxE8gVY6xjNuo55D", 1044 | "BVviCbtT5swCamUBNYk2kHdmbrfrTWG2BBAPb4E4HCzw", 1045 | "BcWWMHWPmhKP6quKZ6oRq3gPoViSGxnAte9Hjhyu89gQ", 1046 | "EozAbbd6CZGE6oE1BphpWZvHYC1bNjB6Kt5YZehdCSoD", 1047 | "CGtCY11cksCvS7TRhbrmVJDtxzRgicMG3Xd3mKVnzyZK", 1048 | "SmhTJyfrhC3TLwJrr8vLhr5vn1SAxGgfctNX2FWLfw6" 1049 | ] --------------------------------------------------------------------------------