From 66247c739ad743b9eda2776c80a1e199900dd896 Mon Sep 17 00:00:00 2001 From: Chewing Glass Date: Fri, 2 Jun 2023 15:11:00 -0500 Subject: [PATCH] Revert "Add support for data only hotspots (#246)" This reverts commit 91b49fae6f5495713559399e61aea653598eb22d. --- .../workflows/develop-release-program.yaml | 8 +- .github/workflows/tests.yaml | 20 - .../helium-admin-cli/assets/data-only.json | 5 - packages/helium-admin-cli/src/create-dao.ts | 76 ---- .../helium-admin-cli/src/create-subdao.ts | 2 - .../helium-admin-cli/src/update-subdao.ts | 7 - .../helium-entity-manager-sdk/src/pdas.ts | 16 +- .../src/resolvers.ts | 6 - programs/helium-entity-manager/src/error.rs | 6 - .../instructions/initialize_data_only_v0.rs | 206 --------- .../instructions/issue_data_only_entity_v0.rs | 217 --------- .../src/instructions/mod.rs | 8 - .../onboard_data_only_iot_hotspot_v0.rs | 183 -------- .../instructions/onboard_iot_hotspot_v0.rs | 11 +- .../instructions/update_data_only_tree_v0.rs | 117 ----- .../src/instructions/update_iot_info_v0.rs | 16 +- programs/helium-entity-manager/src/lib.rs | 27 -- programs/helium-entity-manager/src/state.rs | 39 -- .../src/instructions/initialize_sub_dao_v0.rs | 2 - .../src/instructions/update_sub_dao_v0.rs | 5 - programs/helium-sub-daos/src/state.rs | 1 - tests/helium-entity-manager.ts | 413 +++++++----------- tests/helium-sub-daos.ts | 1 - tests/utils/compression.ts | 106 +---- tests/utils/daos.ts | 1 - utils/ecc-sig-verifier/src/main.rs | 39 +- 26 files changed, 206 insertions(+), 1332 deletions(-) delete mode 100644 packages/helium-admin-cli/assets/data-only.json delete mode 100644 programs/helium-entity-manager/src/instructions/initialize_data_only_v0.rs delete mode 100644 programs/helium-entity-manager/src/instructions/issue_data_only_entity_v0.rs delete mode 100644 programs/helium-entity-manager/src/instructions/onboard_data_only_iot_hotspot_v0.rs delete mode 100644 programs/helium-entity-manager/src/instructions/update_data_only_tree_v0.rs diff --git a/.github/workflows/develop-release-program.yaml b/.github/workflows/develop-release-program.yaml index 8acb3a063..0bb5a8e7b 100644 --- a/.github/workflows/develop-release-program.yaml +++ b/.github/workflows/develop-release-program.yaml @@ -1,16 +1,12 @@ -name: Deploy Programs to devnet +name: Deploy Programs on Develop Push on: push: branches: - develop - pull_request: - branches: - - develop jobs: detect_changed_programs: - if: github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'deploy-to-devnet') runs-on: ubuntu-latest outputs: programs_with_changes: ${{ steps.list_changed_programs.outputs.programs_with_changes }} @@ -24,7 +20,7 @@ jobs: run: | echo "Detecting changes in programs" # Use git diff to get a list of changed programs and output it as JSON - changed_files=$(git diff --name-only ${{ (github.event_name == 'pull_request' && github.event.pull_request.base.sha) || github.event.before }} ${{ github.event.after }}) + changed_files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }}) changed_programs=($(echo "$changed_files" | grep "^programs/" | cut -d '/' -f 2 | sort -u)) echo "${changed_programs[@]}" json="[$(printf "'%s'", "${changed_programs[@]}" | sed 's/,$//')]" diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 374021717..df9e9b02f 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -59,26 +59,6 @@ jobs: with: testing: true - test-devflow: - needs: build - name: Test Development Workflow - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/build-anchor/ - with: - testing: true - - name: Start Anchor Localnet - run: ~/.cargo/bin/anchor localnet --skip-build --provider.wallet ~/.config/solana/id.json & sleep 2 - - name: Wait for localnet to start - run: | - while [[ "$(curl -s http://localhost:8899/health)" != "ok" ]]; do - echo "Waiting for local Anchor network to start..." - sleep 5 - done - - name: Run bootstrap script - run: ./scripts/bootstrap.sh - test-contracts: needs: build name: Test Anchor Contracts diff --git a/packages/helium-admin-cli/assets/data-only.json b/packages/helium-admin-cli/assets/data-only.json deleted file mode 100644 index 299c96289..000000000 --- a/packages/helium-admin-cli/assets/data-only.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Data Only Hotspots", - "symbol": "DATAONLY", - "description": "https://helium.com" -} \ No newline at end of file diff --git a/packages/helium-admin-cli/src/create-dao.ts b/packages/helium-admin-cli/src/create-dao.ts index 5cfcfbf3a..af1ed55fa 100644 --- a/packages/helium-admin-cli/src/create-dao.ts +++ b/packages/helium-admin-cli/src/create-dao.ts @@ -6,10 +6,6 @@ import { PROGRAM_ID, accountPayerKey, } from "@helium/data-credits-sdk"; -import { - init as initHem, - dataOnlyConfigKey, -} from "@helium/helium-entity-manager-sdk"; import { fanoutKey } from "@helium/fanout-sdk"; import { daoKey, @@ -39,7 +35,6 @@ import { import { ComputeBudgetProgram, Connection, - Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, @@ -58,9 +53,6 @@ import { parseEmissionsSchedule, sendInstructionsOrSquads, } from "./utils"; -import fs from "fs"; -import { BN } from "bn.js"; -import { getConcurrentMerkleTreeAccountSize, SPL_ACCOUNT_COMPRESSION_PROGRAM_ID } from "@solana/spl-account-compression"; const { hideBin } = require("yargs/helpers"); @@ -178,11 +170,6 @@ export async function run(args: any = process.argv) { "Number of HST tokens to pre mint before assigning authority to lazy distributor", default: 0, }, - merklePath: { - type: "string", - describe: "Path to the merkle keypair", - default: `${__dirname}/../../keypairs/data-only-merkle.json`, - } }); const argv = await yarg.argv; @@ -194,8 +181,6 @@ export async function run(args: any = process.argv) { const dataCreditsProgram = await initDc(provider); const heliumSubDaosProgram = await initDao(provider); const heliumVsrProgram = await initVsr(provider); - const hemProgram = await initHem(provider); - const govProgramId = new PublicKey(argv.govProgramId); const councilKeypair = await loadKeypair(argv.councilKeypair); @@ -490,65 +475,4 @@ export async function run(args: any = process.argv) { signers: [], }); } - - if (!(await exists(conn, dataOnlyConfigKey(dao)[0]))) { - console.log(`Initializing DataOnly Config`); - let merkle: Keypair; - if (fs.existsSync(argv.merklePath)) { - merkle = loadKeypair(argv.merklePath); - } else { - merkle = Keypair.generate(); - fs.writeFileSync( - argv.merklePath, - JSON.stringify(Array.from(merkle.secretKey)) - ); - } - const [size, buffer, canopy] = [14, 64, 11]; - const space = getConcurrentMerkleTreeAccountSize(size, buffer, canopy); - const cost = await provider.connection.getMinimumBalanceForRentExemption( - space - ); - if (!(await exists(conn, merkle.publicKey))) { - await sendInstructions( - provider, - [ - SystemProgram.createAccount({ - fromPubkey: provider.wallet.publicKey, - newAccountPubkey: merkle.publicKey, - lamports: cost, - space: space, - programId: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, - }), - ], - [merkle] - ); - } - await sendInstructionsOrSquads({ - provider, - instructions: [ - ComputeBudgetProgram.setComputeUnitLimit({ units: 400000 }), - await hemProgram.methods - .initializeDataOnlyV0({ - authority, - newTreeDepth: size, - newTreeBufferSize: buffer, - newTreeSpace: new BN(getConcurrentMerkleTreeAccountSize(size, buffer, canopy)), - newTreeFeeLamports: new BN(cost / 2 ** size), - name: "DATAONLY", - metadataUrl: "https://shdw-drive.genesysgo.net/H8b1gZmA2aBqDYxicxawGpznCaNbFSEJ3YnJuawGQ2EQ/data-only.json", - }) - .accounts({ - dao, - authority, - merkleTree: merkle.publicKey, - }) - .instruction() - ], - executeTransaction: false, - squads, - multisig: argv.multisig ? new PublicKey(argv.multisig) : undefined, - authorityIndex: argv.authorityIndex, - signers: [], - }); - } } diff --git a/packages/helium-admin-cli/src/create-subdao.ts b/packages/helium-admin-cli/src/create-subdao.ts index df6070bfc..f11e0c8ec 100644 --- a/packages/helium-admin-cli/src/create-subdao.ts +++ b/packages/helium-admin-cli/src/create-subdao.ts @@ -469,7 +469,6 @@ export async function run(args: any = process.argv) { // $40 for iot, $0 for mobile onboardingDcFee: name.toUpperCase() == "IOT" ? toBN(4000000, 0) : toBN(0, 0), - onboardingDataOnlyDcFee: name.toUpperCase() == "IOT" ? toBN(1000000, 0) : toBN(0, 0), delegatorRewardsPercent: delegatorRewardsPercent( argv.delegatorRewardsPercent ), @@ -538,7 +537,6 @@ export async function run(args: any = process.argv) { emissionSchedule, dcBurnAuthority: null, onboardingDcFee: null, - onboardingDataOnlyDcFee: null, activeDeviceAggregator: null, registrar: null, delegatorRewardsPercent: null, diff --git a/packages/helium-admin-cli/src/update-subdao.ts b/packages/helium-admin-cli/src/update-subdao.ts index 5f5e813d0..283014a92 100644 --- a/packages/helium-admin-cli/src/update-subdao.ts +++ b/packages/helium-admin-cli/src/update-subdao.ts @@ -101,12 +101,6 @@ export async function run(args: any = process.argv) { "Percentage of rewards allocated to delegators. Must be between 0-100 and can have 8 decimal places.", default: null, }, - onboardingDataOnlyDcFee: { - type: "number", - required: false, - describe: "The data credits fee for onboarding data only hotspots", - default: null, - } }); const argv = await yarg.argv; process.env.ANCHOR_WALLET = argv.wallet; @@ -223,7 +217,6 @@ export async function run(args: any = process.argv) { ? new PublicKey(argv.newDcBurnAuthority) : null, onboardingDcFee: null, - onboardingDataOnlyDcFee: argv.onboardingDataOnlyDcFee ? new BN(argv.onboardingDataOnlyDcFee) : null, activeDeviceAggregator: argv.newActiveDeviceAggregator ? new PublicKey(argv.newActiveDeviceAggregator) : null, diff --git a/packages/helium-entity-manager-sdk/src/pdas.ts b/packages/helium-entity-manager-sdk/src/pdas.ts index c3d824cff..de48717a2 100644 --- a/packages/helium-entity-manager-sdk/src/pdas.ts +++ b/packages/helium-entity-manager-sdk/src/pdas.ts @@ -27,26 +27,14 @@ export const rewardableEntityConfigKey = ( ); export const hotspotCollectionKey = ( - makerOrDataOnly: PublicKey, + maker: PublicKey, programId: PublicKey = PROGRAM_ID ) => PublicKey.findProgramAddressSync( - [Buffer.from("collection", "utf-8"), makerOrDataOnly.toBuffer()], + [Buffer.from("collection", "utf-8"), maker.toBuffer()], programId ); -export const dataOnlyConfigKey = (dao: PublicKey, programId: PublicKey = PROGRAM_ID) => - PublicKey.findProgramAddressSync( - [Buffer.from("data_only_config", "utf-8"), dao.toBuffer()], - programId, - ); - -export const dataOnlyEscrowKey = (dataOnly: PublicKey, programId: PublicKey = PROGRAM_ID) => - PublicKey.findProgramAddressSync( - [Buffer.from("data_only_escrow", "utf-8"), dataOnly.toBuffer()], - programId, - ); - export const makerKey = (dao: PublicKey, name: String, programId: PublicKey = PROGRAM_ID) => PublicKey.findProgramAddressSync( [Buffer.from("maker", "utf-8"), dao.toBuffer(), Buffer.from(name, "utf-8")], diff --git a/packages/helium-entity-manager-sdk/src/resolvers.ts b/packages/helium-entity-manager-sdk/src/resolvers.ts index 86f34e711..93f4a282b 100644 --- a/packages/helium-entity-manager-sdk/src/resolvers.ts +++ b/packages/helium-entity-manager-sdk/src/resolvers.ts @@ -18,12 +18,6 @@ export const heliumEntityManagerResolvers = combineResolvers( mint: "collection", owner: "maker", }), - ataResolver({ - instruction: "initializeDataOnlyV0", - account: "tokenAccount", - mint: "collection", - owner: "dataOnlyConfig", - }), resolveIndividual(async ({ path, args, accounts, provider }) => { if (path[path.length - 1] == "programApproval" && accounts.dao) { let programId = args[args.length - 1] && args[args.length - 1].programId; diff --git a/programs/helium-entity-manager/src/error.rs b/programs/helium-entity-manager/src/error.rs index 3939fe160..fb6bb99e8 100644 --- a/programs/helium-entity-manager/src/error.rs +++ b/programs/helium-entity-manager/src/error.rs @@ -20,12 +20,6 @@ pub enum ErrorCode { #[msg("Genesis endpoints are currently disabled")] NoGenesis, - #[msg("The tree can only be replaced when it is close to full")] - TreeNotFull, - - #[msg("The provided tree is an invalid size")] - InvalidTreeSpace, - #[msg("Invalid seeds provided")] InvalidSeeds, } diff --git a/programs/helium-entity-manager/src/instructions/initialize_data_only_v0.rs b/programs/helium-entity-manager/src/instructions/initialize_data_only_v0.rs deleted file mode 100644 index 09c030522..000000000 --- a/programs/helium-entity-manager/src/instructions/initialize_data_only_v0.rs +++ /dev/null @@ -1,206 +0,0 @@ -use crate::error::ErrorCode; -use crate::{data_only_config_seeds, state::*}; -use anchor_lang::prelude::*; -use anchor_spl::metadata::{ - create_master_edition_v3, CreateMasterEditionV3, CreateMetadataAccountsV3, -}; -use anchor_spl::{ - associated_token::AssociatedToken, - token::{self, Mint, MintTo, Token, TokenAccount}, -}; -use helium_sub_daos::DaoV0; -use mpl_bubblegum::{ - cpi::{accounts::CreateTree, create_tree}, - program::Bubblegum, -}; -use mpl_token_metadata::state::{CollectionDetails, DataV2}; -use shared_utils::create_metadata_accounts_v3; -use spl_account_compression::{program::SplAccountCompression, Noop}; - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)] -pub struct InitializeDataOnlyArgsV0 { - pub authority: Pubkey, - pub new_tree_depth: u32, - pub new_tree_buffer_size: u32, - pub new_tree_space: u64, - pub new_tree_fee_lamports: u64, - pub name: String, - pub metadata_url: String, -} - -#[derive(Accounts)] -#[instruction(args: InitializeDataOnlyArgsV0)] -pub struct InitializeDataOnlyV0<'info> { - #[account(mut)] - pub authority: Signer<'info>, - #[account( - init, - space = 8 + 60 + std::mem::size_of::(), - payer = authority, - seeds = ["data_only_config".as_bytes(), dao.key().as_ref()], - bump, - )] - pub data_only_config: Box>, - #[account(has_one = authority)] - pub dao: Account<'info, DaoV0>, - #[account( - mut, - seeds = [merkle_tree.key().as_ref()], - bump, - seeds::program = bubblegum_program.key() - )] - /// CHECK: Checked by cpi - pub tree_authority: AccountInfo<'info>, - - /// CHECK: Checked by cpi - #[account(mut)] - pub merkle_tree: AccountInfo<'info>, - - #[account( - init, - payer = authority, - mint::decimals = 0, - mint::authority = data_only_config, - mint::freeze_authority = data_only_config, - seeds = ["collection".as_bytes(), data_only_config.key().as_ref()], - bump - )] - pub collection: Box>, - #[account( - init_if_needed, - payer = authority, - associated_token::mint = collection, - associated_token::authority = data_only_config, - )] - pub token_account: Box>, - /// CHECK: Handled by cpi - #[account( - mut, - seeds = ["metadata".as_bytes(), token_metadata_program.key().as_ref(), collection.key().as_ref(), "edition".as_bytes()], - seeds::program = token_metadata_program.key(), - bump, - )] - pub master_edition: UncheckedAccount<'info>, - /// CHECK: Handled by cpi - #[account( - mut, - seeds = ["metadata".as_bytes(), token_metadata_program.key().as_ref(), collection.key().as_ref()], - seeds::program = token_metadata_program.key(), - bump, - )] - pub metadata: UncheckedAccount<'info>, - /// CHECK: Checked with constraints - #[account(address = mpl_token_metadata::ID)] - pub token_metadata_program: AccountInfo<'info>, - pub log_wrapper: Program<'info, Noop>, - pub system_program: Program<'info, System>, - pub bubblegum_program: Program<'info, Bubblegum>, - pub compression_program: Program<'info, SplAccountCompression>, - pub token_program: Program<'info, Token>, - pub associated_token_program: Program<'info, AssociatedToken>, - pub rent: Sysvar<'info, Rent>, -} - -pub fn handler(ctx: Context, args: InitializeDataOnlyArgsV0) -> Result<()> { - require!(args.name.len() <= 32, ErrorCode::InvalidStringLength); - require!( - args.metadata_url.len() <= 200, - ErrorCode::InvalidStringLength - ); - - let dao = ctx.accounts.dao.key(); - ctx.accounts.data_only_config.set_inner(DataOnlyConfigV0 { - authority: args.authority, - collection: ctx.accounts.collection.key(), - merkle_tree: Pubkey::default(), - bump_seed: ctx.bumps["data_only_config"], - collection_bump_seed: ctx.bumps["collection"], - dao, - new_tree_depth: args.new_tree_depth, - new_tree_buffer_size: args.new_tree_buffer_size, - new_tree_space: args.new_tree_space, - new_tree_fee_lamports: args.new_tree_fee_lamports, - }); - let signer_seeds: &[&[&[u8]]] = &[data_only_config_seeds!(ctx.accounts.data_only_config)]; - - token::mint_to( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - MintTo { - mint: ctx.accounts.collection.to_account_info(), - to: ctx.accounts.token_account.to_account_info(), - authority: ctx.accounts.data_only_config.to_account_info(), - }, - ) - .with_signer(signer_seeds), - 1, - )?; - - create_metadata_accounts_v3( - CpiContext::new_with_signer( - ctx.accounts.token_metadata_program.clone(), - CreateMetadataAccountsV3 { - metadata: ctx.accounts.metadata.to_account_info().clone(), - mint: ctx.accounts.collection.to_account_info().clone(), - mint_authority: ctx.accounts.data_only_config.to_account_info().clone(), - payer: ctx.accounts.authority.to_account_info().clone(), - update_authority: ctx.accounts.data_only_config.to_account_info().clone(), - system_program: ctx.accounts.system_program.to_account_info().clone(), - rent: ctx.accounts.rent.to_account_info().clone(), - }, - signer_seeds, - ), - DataV2 { - name: args.name.clone(), - symbol: "DATAONLY".to_string(), - uri: args.metadata_url, - seller_fee_basis_points: 0, - creators: None, - collection: None, - uses: None, - }, - true, - true, - Some(CollectionDetails::V1 { size: 0 }), - )?; - - create_master_edition_v3( - CpiContext::new_with_signer( - ctx.accounts.token_metadata_program.clone(), - CreateMasterEditionV3 { - edition: ctx.accounts.master_edition.to_account_info().clone(), - mint: ctx.accounts.collection.to_account_info().clone(), - update_authority: ctx.accounts.data_only_config.to_account_info().clone(), - mint_authority: ctx.accounts.data_only_config.to_account_info().clone(), - metadata: ctx.accounts.metadata.to_account_info().clone(), - payer: ctx.accounts.authority.to_account_info().clone(), - token_program: ctx.accounts.token_program.to_account_info().clone(), - system_program: ctx.accounts.system_program.to_account_info().clone(), - rent: ctx.accounts.rent.to_account_info().clone(), - }, - signer_seeds, - ), - Some(0), - )?; - - create_tree( - CpiContext::new_with_signer( - ctx.accounts.bubblegum_program.to_account_info().clone(), - CreateTree { - tree_authority: ctx.accounts.tree_authority.to_account_info().clone(), - merkle_tree: ctx.accounts.merkle_tree.to_account_info().clone(), - payer: ctx.accounts.authority.to_account_info().clone(), - tree_creator: ctx.accounts.data_only_config.to_account_info().clone(), - log_wrapper: ctx.accounts.log_wrapper.to_account_info().clone(), - compression_program: ctx.accounts.compression_program.to_account_info().clone(), - system_program: ctx.accounts.system_program.to_account_info().clone(), - }, - signer_seeds, - ), - args.new_tree_depth, - args.new_tree_buffer_size, - None, - )?; - ctx.accounts.data_only_config.merkle_tree = ctx.accounts.merkle_tree.key(); - Ok(()) -} diff --git a/programs/helium-entity-manager/src/instructions/issue_data_only_entity_v0.rs b/programs/helium-entity-manager/src/instructions/issue_data_only_entity_v0.rs deleted file mode 100644 index 28180ab89..000000000 --- a/programs/helium-entity-manager/src/instructions/issue_data_only_entity_v0.rs +++ /dev/null @@ -1,217 +0,0 @@ -use std::cmp::min; -use std::str::FromStr; - -use crate::data_only_config_seeds; -use crate::state::*; -use crate::ECC_VERIFIER; -use crate::{constants::ENTITY_METADATA_URL, error::ErrorCode}; -use anchor_lang::prelude::*; -use anchor_lang::solana_program::hash::hash; -use anchor_lang::solana_program::program::invoke; -use anchor_lang::solana_program::system_instruction::{self}; -use anchor_spl::token::Mint; -use angry_purple_tiger::AnimalName; -use helium_sub_daos::DaoV0; -use mpl_bubblegum::state::metaplex_adapter::{ - Collection, Creator, MetadataArgs, TokenProgramVersion, -}; -use mpl_bubblegum::state::{metaplex_adapter::TokenStandard, TreeConfig}; -use mpl_bubblegum::utils::get_asset_id; -use mpl_bubblegum::{ - cpi::{accounts::MintToCollectionV1, mint_to_collection_v1}, - program::Bubblegum, -}; -use spl_account_compression::{program::SplAccountCompression, Noop}; - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)] -pub struct IssueDataOnlyEntityArgsV0 { - pub entity_key: Vec, -} - -#[derive(Accounts)] -#[instruction(args: IssueDataOnlyEntityArgsV0)] -pub struct IssueDataOnlyEntityV0<'info> { - #[account(mut)] - pub payer: Signer<'info>, - #[account(address = Pubkey::from_str(ECC_VERIFIER).unwrap())] - pub ecc_verifier: Signer<'info>, - pub collection: Box>, - /// CHECK: Handled by cpi - #[account( - mut, - seeds = ["metadata".as_bytes(), token_metadata_program.key().as_ref(), collection.key().as_ref()], - seeds::program = token_metadata_program.key(), - bump, - )] - pub collection_metadata: UncheckedAccount<'info>, - /// CHECK: Handled By cpi account - #[account( - seeds = ["metadata".as_bytes(), token_metadata_program.key().as_ref(), collection.key().as_ref(), "edition".as_bytes()], - seeds::program = token_metadata_program.key(), - bump, - )] - pub collection_master_edition: UncheckedAccount<'info>, - #[account( - mut, - seeds = ["data_only_config".as_bytes(), dao.key().as_ref()], - bump, - has_one = collection, - has_one = merkle_tree, - has_one = dao, - )] - pub data_only_config: Box>, - /// CHECK: Signs as a verified creator to make searching easier - #[account( - seeds = ["entity_creator".as_bytes(), dao.key().as_ref()], - bump, - )] - pub entity_creator: UncheckedAccount<'info>, - pub dao: Box>, - #[account( - init, - payer = payer, - space = 8 + std::mem::size_of::() + 1 + args.entity_key.len(), - seeds = [ - "key_to_asset".as_bytes(), - dao.key().as_ref(), - &hash(&args.entity_key[..]).to_bytes() - ], - bump - )] - pub key_to_asset: Box>, - #[account( - mut, - seeds = [merkle_tree.key().as_ref()], - seeds::program = bubblegum_program.key(), - bump, - )] - pub tree_authority: Box>, - /// CHECK: Used in cpi - pub recipient: AccountInfo<'info>, - /// CHECK: Used in cpi - #[account(mut)] - pub merkle_tree: AccountInfo<'info>, - - /// CHECK: checked with seeds - #[account(mut, - seeds = [ - "data_only_escrow".as_bytes(), - data_only_config.key().as_ref(), - ], - bump - )] - pub data_only_escrow: AccountInfo<'info>, - #[account( - seeds = ["collection_cpi".as_bytes()], - seeds::program = bubblegum_program.key(), - bump, - )] - /// CHECK: Used in cpi - pub bubblegum_signer: UncheckedAccount<'info>, - - /// CHECK: Verified by constraint - #[account(address = mpl_token_metadata::ID)] - pub token_metadata_program: AccountInfo<'info>, - pub log_wrapper: Program<'info, Noop>, - pub bubblegum_program: Program<'info, Bubblegum>, - pub compression_program: Program<'info, SplAccountCompression>, - pub system_program: Program<'info, System>, -} - -impl<'info> IssueDataOnlyEntityV0<'info> { - fn mint_to_collection_ctx(&self) -> CpiContext<'_, '_, '_, 'info, MintToCollectionV1<'info>> { - let cpi_accounts = MintToCollectionV1 { - tree_authority: self.tree_authority.to_account_info(), - leaf_delegate: self.recipient.to_account_info(), - leaf_owner: self.recipient.to_account_info(), - merkle_tree: self.merkle_tree.to_account_info(), - payer: self.payer.to_account_info(), - tree_delegate: self.data_only_config.to_account_info(), - log_wrapper: self.log_wrapper.to_account_info(), - compression_program: self.compression_program.to_account_info(), - system_program: self.system_program.to_account_info(), - collection_authority: self.data_only_config.to_account_info(), - collection_authority_record_pda: self.bubblegum_program.to_account_info(), - collection_mint: self.collection.to_account_info(), - collection_metadata: self.collection_metadata.to_account_info(), - edition_account: self.collection_master_edition.to_account_info(), - bubblegum_signer: self.bubblegum_signer.to_account_info(), - token_metadata_program: self.token_metadata_program.to_account_info(), - }; - CpiContext::new(self.bubblegum_program.to_account_info(), cpi_accounts) - } -} - -pub fn handler(ctx: Context, args: IssueDataOnlyEntityArgsV0) -> Result<()> { - let key_str = bs58::encode(args.entity_key.clone()).into_string(); - let animal_name: AnimalName = key_str - .parse() - .map_err(|_| error!(ErrorCode::InvalidEccCompact))?; - - let data_only_seeds: &[&[&[u8]]] = &[data_only_config_seeds!(ctx.accounts.data_only_config)]; - let asset_id = get_asset_id( - &ctx.accounts.merkle_tree.key(), - ctx.accounts.tree_authority.num_minted, - ); - - let name = animal_name.to_string(); - let metadata = MetadataArgs { - name: name[..min(name.len(), 32)].to_owned(), - symbol: String::from("HOTSPOT"), - uri: format!("{}/{}", ENTITY_METADATA_URL, key_str), - collection: Some(Collection { - key: ctx.accounts.collection.key(), - verified: false, // Verified in cpi - }), - primary_sale_happened: true, - is_mutable: true, - edition_nonce: None, - token_standard: Some(TokenStandard::NonFungible), - uses: None, - token_program_version: TokenProgramVersion::Original, - creators: vec![Creator { - address: ctx.accounts.entity_creator.key(), - verified: true, - share: 100, - }], - seller_fee_basis_points: 0, - }; - let entity_creator_seeds: &[&[&[u8]]] = &[&[ - b"entity_creator", - ctx.accounts.dao.to_account_info().key.as_ref(), - &[ctx.bumps["entity_creator"]], - ]]; - let mut creator = ctx.accounts.entity_creator.to_account_info(); - creator.is_signer = true; - - mint_to_collection_v1( - ctx - .accounts - .mint_to_collection_ctx() - .with_remaining_accounts(vec![creator]) - .with_signer(&[data_only_seeds[0], entity_creator_seeds[0]]), - metadata, - )?; - - ctx.accounts.key_to_asset.set_inner(KeyToAssetV0 { - asset: asset_id, - dao: ctx.accounts.dao.key(), - entity_key: args.entity_key, - bump_seed: ctx.bumps["key_to_asset"], - key_serialization: KeySerialization::B58, - }); - - invoke( - &system_instruction::transfer( - ctx.accounts.payer.key, - ctx.accounts.data_only_escrow.key, - ctx.accounts.data_only_config.new_tree_fee_lamports, - ), - &[ - ctx.accounts.payer.to_account_info().clone(), - ctx.accounts.data_only_escrow.to_account_info().clone(), - ctx.accounts.system_program.to_account_info().clone(), - ], - )?; - Ok(()) -} diff --git a/programs/helium-entity-manager/src/instructions/mod.rs b/programs/helium-entity-manager/src/instructions/mod.rs index 629a1ab23..5fd3cf199 100644 --- a/programs/helium-entity-manager/src/instructions/mod.rs +++ b/programs/helium-entity-manager/src/instructions/mod.rs @@ -1,21 +1,17 @@ pub mod approve_maker_v0; pub mod approve_program_v0; pub mod genesis_issue_hotspot_v0; -pub mod initialize_data_only_v0; pub mod initialize_maker_v0; pub mod initialize_rewardable_entity_config_v0; -pub mod issue_data_only_entity_v0; pub mod issue_entity_v0; pub mod issue_iot_operations_fund_v0; pub mod issue_program_entity_v0; -pub mod onboard_data_only_iot_hotspot_v0; pub mod onboard_iot_hotspot_v0; pub mod onboard_mobile_hotspot_v0; pub mod revoke_maker_v0; pub mod revoke_program_v0; pub mod set_maker_tree_v0; pub mod temp_repair_iot_operations_fund; -pub mod update_data_only_tree_v0; pub mod update_iot_info_v0; pub mod update_maker_tree_v0; pub mod update_maker_v0; @@ -25,21 +21,17 @@ pub mod update_rewardable_entity_config_v0; pub use approve_maker_v0::*; pub use approve_program_v0::*; pub use genesis_issue_hotspot_v0::*; -pub use initialize_data_only_v0::*; pub use initialize_maker_v0::*; pub use initialize_rewardable_entity_config_v0::*; -pub use issue_data_only_entity_v0::*; pub use issue_entity_v0::*; pub use issue_iot_operations_fund_v0::*; pub use issue_program_entity_v0::*; -pub use onboard_data_only_iot_hotspot_v0::*; pub use onboard_iot_hotspot_v0::*; pub use onboard_mobile_hotspot_v0::*; pub use revoke_maker_v0::*; pub use revoke_program_v0::*; pub use set_maker_tree_v0::*; pub use temp_repair_iot_operations_fund::*; -pub use update_data_only_tree_v0::*; pub use update_iot_info_v0::*; pub use update_maker_tree_v0::*; pub use update_maker_v0::*; diff --git a/programs/helium-entity-manager/src/instructions/onboard_data_only_iot_hotspot_v0.rs b/programs/helium-entity-manager/src/instructions/onboard_data_only_iot_hotspot_v0.rs deleted file mode 100644 index 5bb8d39ec..000000000 --- a/programs/helium-entity-manager/src/instructions/onboard_data_only_iot_hotspot_v0.rs +++ /dev/null @@ -1,183 +0,0 @@ -use crate::state::*; -use anchor_lang::{prelude::*, solana_program::hash::hash}; - -use anchor_spl::{ - associated_token::AssociatedToken, - token::{Mint, Token, TokenAccount}, -}; -use data_credits::{ - cpi::{ - accounts::{BurnCommonV0, BurnWithoutTrackingV0}, - burn_without_tracking_v0, - }, - program::DataCredits, - BurnWithoutTrackingArgsV0, DataCreditsV0, -}; -use helium_sub_daos::{DaoV0, SubDaoV0}; -use mpl_bubblegum::utils::get_asset_id; -use shared_utils::*; -use spl_account_compression::program::SplAccountCompression; - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)] -pub struct OnboardDataOnlyIotHotspotArgsV0 { - pub data_hash: [u8; 32], - pub creator_hash: [u8; 32], - pub root: [u8; 32], - pub index: u32, - pub location: Option, - pub elevation: Option, - pub gain: Option, -} - -#[derive(Accounts)] -#[instruction(args: OnboardDataOnlyIotHotspotArgsV0)] -pub struct OnboardDataOnlyIotHotspotV0<'info> { - #[account(mut)] - pub payer: Signer<'info>, - #[account(mut)] - pub dc_fee_payer: Signer<'info>, - #[account( - init, - payer = payer, - space = IOT_HOTSPOT_INFO_SIZE, - seeds = [ - b"iot_info", - rewardable_entity_config.key().as_ref(), - &hash(&key_to_asset.entity_key[..]).to_bytes() - ], - bump, - )] - pub iot_info: Box>, - #[account(mut)] - pub hotspot_owner: Signer<'info>, - /// CHECK: The merkle tree - pub merkle_tree: UncheckedAccount<'info>, - #[account( - mut, - associated_token::mint = dc_mint, - associated_token::authority = dc_fee_payer, - )] - pub dc_burner: Box>, - - #[account( - has_one = sub_dao, - constraint = rewardable_entity_config.settings.validate_iot_gain(args.gain), - )] - pub rewardable_entity_config: Box>, - - #[account( - mut, - seeds = ["data_only_config".as_bytes(), dao.key().as_ref()], - bump, - has_one = merkle_tree, - has_one = dao, - )] - pub data_only_config: Box>, - #[account( - has_one = dc_mint, - )] - pub dao: Box>, - #[account( - has_one = dao, - constraint = get_asset_id(&merkle_tree.key(), args.index.into()) == key_to_asset.asset, - )] - pub key_to_asset: Box>, - #[account( - has_one = dao, - )] - pub sub_dao: Box>, - #[account(mut)] - pub dc_mint: Box>, - - #[account( - seeds = [ - "dc".as_bytes(), - dc_mint.key().as_ref() - ], - seeds::program = data_credits_program.key(), - bump = dc.data_credits_bump, - has_one = dc_mint - )] - pub dc: Account<'info, DataCreditsV0>, - - pub compression_program: Program<'info, SplAccountCompression>, - pub data_credits_program: Program<'info, DataCredits>, - pub token_program: Program<'info, Token>, - pub associated_token_program: Program<'info, AssociatedToken>, - pub system_program: Program<'info, System>, -} - -impl<'info> OnboardDataOnlyIotHotspotV0<'info> { - pub fn burn_ctx(&self) -> CpiContext<'_, '_, '_, 'info, BurnWithoutTrackingV0<'info>> { - let cpi_accounts = BurnWithoutTrackingV0 { - burn_accounts: BurnCommonV0 { - data_credits: self.dc.to_account_info(), - burner: self.dc_burner.to_account_info(), - owner: self.dc_fee_payer.to_account_info(), - dc_mint: self.dc_mint.to_account_info(), - token_program: self.token_program.to_account_info(), - associated_token_program: self.associated_token_program.to_account_info(), - system_program: self.system_program.to_account_info(), - }, - }; - - CpiContext::new(self.token_program.to_account_info(), cpi_accounts) - } -} - -pub fn handler<'info>( - ctx: Context<'_, '_, '_, 'info, OnboardDataOnlyIotHotspotV0<'info>>, - args: OnboardDataOnlyIotHotspotArgsV0, -) -> Result<()> { - let asset_id = get_asset_id(&ctx.accounts.merkle_tree.key(), args.index.into()); - verify_compressed_nft(VerifyCompressedNftArgs { - data_hash: args.data_hash, - creator_hash: args.creator_hash, - root: args.root, - index: args.index, - compression_program: ctx.accounts.compression_program.to_account_info(), - merkle_tree: ctx.accounts.merkle_tree.to_account_info(), - owner: ctx.accounts.hotspot_owner.key(), - delegate: ctx.accounts.hotspot_owner.key(), - proof_accounts: ctx.remaining_accounts.to_vec(), - })?; - - let mut dc_fee = ctx.accounts.sub_dao.onboarding_data_only_dc_fee; - ctx.accounts.iot_info.set_inner(IotHotspotInfoV0 { - asset: asset_id, - bump_seed: ctx.bumps["iot_info"], - location: None, - elevation: args.elevation, - gain: args.gain, - is_full_hotspot: false, - num_location_asserts: 0, - }); - - if let ( - Some(location), - ConfigSettingsV0::IotConfig { - dataonly_location_staking_fee, - .. - }, - ) = ( - args.location, - ctx.accounts.rewardable_entity_config.settings, - ) { - dc_fee = dataonly_location_staking_fee.checked_add(dc_fee).unwrap(); - - ctx.accounts.iot_info.location = Some(location); - ctx.accounts.iot_info.num_location_asserts = ctx - .accounts - .iot_info - .num_location_asserts - .checked_add(1) - .unwrap(); - } - - burn_without_tracking_v0( - ctx.accounts.burn_ctx(), - BurnWithoutTrackingArgsV0 { amount: dc_fee }, - )?; - - Ok(()) -} diff --git a/programs/helium-entity-manager/src/instructions/onboard_iot_hotspot_v0.rs b/programs/helium-entity-manager/src/instructions/onboard_iot_hotspot_v0.rs index 9cb82e74a..2b8f190ca 100644 --- a/programs/helium-entity-manager/src/instructions/onboard_iot_hotspot_v0.rs +++ b/programs/helium-entity-manager/src/instructions/onboard_iot_hotspot_v0.rs @@ -60,7 +60,16 @@ pub struct OnboardIotHotspotV0<'info> { #[account( has_one = sub_dao, - constraint = rewardable_entity_config.settings.validate_iot_gain(args.gain), + constraint = match rewardable_entity_config.settings { + ConfigSettingsV0::IotConfig { + max_gain, min_gain, .. + } => { + args.gain + .map(|gain| gain <= max_gain && gain >= min_gain) + .unwrap_or(true) + } + _ => false, + } )] pub rewardable_entity_config: Box>, #[account( diff --git a/programs/helium-entity-manager/src/instructions/update_data_only_tree_v0.rs b/programs/helium-entity-manager/src/instructions/update_data_only_tree_v0.rs deleted file mode 100644 index dfa8bd477..000000000 --- a/programs/helium-entity-manager/src/instructions/update_data_only_tree_v0.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::error::ErrorCode; -use crate::{data_only_config_seeds, state::*}; -use anchor_lang::prelude::*; -use anchor_lang::solana_program::program::invoke_signed; -use anchor_lang::solana_program::system_instruction::{self}; -use mpl_bubblegum::state::TreeConfig; -use mpl_bubblegum::{ - cpi::{accounts::CreateTree, create_tree}, - program::Bubblegum, -}; -use spl_account_compression::{program::SplAccountCompression, Noop}; - -#[derive(Accounts)] -pub struct UpdateDataOnlyTreeV0<'info> { - #[account(mut)] - pub payer: Signer<'info>, - #[account(mut)] - pub data_only_config: Box>, - #[account( - mut, - seeds = [data_only_config.merkle_tree.as_ref()], - bump, - seeds::program = bubblegum_program.key(), - )] - pub old_tree_authority: Account<'info, TreeConfig>, - - /// CHECK: Checked by cpi - #[account( - mut, - seeds = [new_merkle_tree.key().as_ref()], - bump, - seeds::program = bubblegum_program.key() - )] - pub new_tree_authority: AccountInfo<'info>, - - /// CHECK: checked with seeds - #[account(mut, - seeds = [ - "data_only_escrow".as_bytes(), - data_only_config.key().as_ref(), - ], - bump - )] - pub data_only_escrow: AccountInfo<'info>, - - /// CHECK: Checked by cpi - #[account(mut)] - pub new_merkle_tree: UncheckedAccount<'info>, - - pub log_wrapper: Program<'info, Noop>, - pub system_program: Program<'info, System>, - pub bubblegum_program: Program<'info, Bubblegum>, - pub compression_program: Program<'info, SplAccountCompression>, -} - -pub fn handler(ctx: Context) -> Result<()> { - require!( - ctx.accounts.new_merkle_tree.to_account_info().data_len() - == ctx.accounts.data_only_config.new_tree_space as usize, - ErrorCode::InvalidTreeSpace - ); - - if !ctx - .accounts - .old_tree_authority - .to_account_info() - .data_is_empty() - { - require_gt!( - ctx.accounts.old_tree_authority.num_minted, - ctx.accounts.old_tree_authority.total_mint_capacity - 5, - ErrorCode::TreeNotFull - ); - } - - let data_only_seeds: &[&[&[u8]]] = &[data_only_config_seeds!(ctx.accounts.data_only_config)]; - create_tree( - CpiContext::new_with_signer( - ctx.accounts.bubblegum_program.to_account_info().clone(), - CreateTree { - tree_authority: ctx.accounts.new_tree_authority.to_account_info().clone(), - merkle_tree: ctx.accounts.new_merkle_tree.to_account_info().clone(), - payer: ctx.accounts.payer.to_account_info().clone(), - tree_creator: ctx.accounts.data_only_config.to_account_info().clone(), - log_wrapper: ctx.accounts.log_wrapper.to_account_info().clone(), - compression_program: ctx.accounts.compression_program.to_account_info().clone(), - system_program: ctx.accounts.system_program.to_account_info().clone(), - }, - data_only_seeds, - ), - ctx.accounts.data_only_config.new_tree_depth, - ctx.accounts.data_only_config.new_tree_buffer_size, - None, - )?; - ctx.accounts.data_only_config.merkle_tree = ctx.accounts.new_merkle_tree.key(); - - // reimburse the payer from the escrow account - let cost = Rent::get()?.minimum_balance(ctx.accounts.data_only_config.new_tree_space as usize); - invoke_signed( - &system_instruction::transfer( - &ctx.accounts.data_only_escrow.key(), - &ctx.accounts.payer.key(), - cost, - ), - &[ - ctx.accounts.data_only_escrow.to_account_info().clone(), - ctx.accounts.payer.to_account_info().clone(), - ctx.accounts.system_program.to_account_info().clone(), - ], - &[&[ - b"data_only_escrow", - ctx.accounts.data_only_config.key().as_ref(), - &[ctx.bumps["data_only_escrow"]], - ]], - )?; - Ok(()) -} diff --git a/programs/helium-entity-manager/src/instructions/update_iot_info_v0.rs b/programs/helium-entity-manager/src/instructions/update_iot_info_v0.rs index 66412b93c..507a9f0c5 100644 --- a/programs/helium-entity-manager/src/instructions/update_iot_info_v0.rs +++ b/programs/helium-entity-manager/src/instructions/update_iot_info_v0.rs @@ -28,6 +28,20 @@ pub struct UpdateIotInfoArgsV0 { pub index: u32, } +impl ConfigSettingsV0 { + pub fn is_valid_iot(self, args: UpdateIotInfoArgsV0) -> bool { + match (args.gain, self) { + ( + Some(gain), + ConfigSettingsV0::IotConfig { + max_gain, min_gain, .. + }, + ) => gain <= max_gain && gain >= min_gain, + _ => true, + } + } +} + #[derive(Accounts)] #[instruction(args: UpdateIotInfoArgsV0)] pub struct UpdateIotInfoV0<'info> { @@ -56,7 +70,7 @@ pub struct UpdateIotInfoV0<'info> { #[account( has_one = sub_dao, - constraint = rewardable_entity_config.settings.validate_iot_gain(args.gain) + constraint = rewardable_entity_config.settings.is_valid_iot(args) )] pub rewardable_entity_config: Box>, #[account( diff --git a/programs/helium-entity-manager/src/lib.rs b/programs/helium-entity-manager/src/lib.rs index 01cf5872f..f6baa0741 100644 --- a/programs/helium-entity-manager/src/lib.rs +++ b/programs/helium-entity-manager/src/lib.rs @@ -119,33 +119,6 @@ pub mod helium_entity_manager { update_mobile_info_v0::handler(ctx, args) } - pub fn initialize_data_only_v0<'info>( - ctx: Context<'_, '_, '_, 'info, InitializeDataOnlyV0<'info>>, - args: InitializeDataOnlyArgsV0, - ) -> Result<()> { - initialize_data_only_v0::handler(ctx, args) - } - - pub fn issue_data_only_entity_v0<'info>( - ctx: Context<'_, '_, '_, 'info, IssueDataOnlyEntityV0<'info>>, - args: IssueDataOnlyEntityArgsV0, - ) -> Result<()> { - issue_data_only_entity_v0::handler(ctx, args) - } - - pub fn onboard_data_only_iot_hotspot_v0<'info>( - ctx: Context<'_, '_, '_, 'info, OnboardDataOnlyIotHotspotV0<'info>>, - args: OnboardDataOnlyIotHotspotArgsV0, - ) -> Result<()> { - onboard_data_only_iot_hotspot_v0::handler(ctx, args) - } - - pub fn update_data_only_tree_v0<'info>( - ctx: Context<'_, '_, '_, 'info, UpdateDataOnlyTreeV0<'info>>, - ) -> Result<()> { - update_data_only_tree_v0::handler(ctx) - } - pub fn temp_repair_iot_operations_fund(ctx: Context) -> Result<()> { temp_repair_iot_operations_fund::handler(ctx) } diff --git a/programs/helium-entity-manager/src/state.rs b/programs/helium-entity-manager/src/state.rs index fad3d7fc7..d90903d09 100644 --- a/programs/helium-entity-manager/src/state.rs +++ b/programs/helium-entity-manager/src/state.rs @@ -25,19 +25,6 @@ pub enum ConfigSettingsV0 { }, } -impl ConfigSettingsV0 { - pub fn validate_iot_gain(&self, gain: Option) -> bool { - match self { - ConfigSettingsV0::IotConfig { - max_gain, min_gain, .. - } => gain - .map(|gain| &gain <= max_gain && &gain >= min_gain) - .unwrap_or(true), - _ => false, - } - } -} - impl Default for ConfigSettingsV0 { fn default() -> Self { ConfigSettingsV0::IotConfig { @@ -70,21 +57,6 @@ pub struct MakerApprovalV0 { pub bump_seed: u8, } -#[account] -#[derive(Default)] -pub struct DataOnlyConfigV0 { - pub authority: Pubkey, - pub bump_seed: u8, - pub collection: Pubkey, // The metaplex collection to be issued - pub merkle_tree: Pubkey, - pub collection_bump_seed: u8, - pub dao: Pubkey, - pub new_tree_depth: u32, // parameters for new merkle trees when old is full - pub new_tree_buffer_size: u32, - pub new_tree_space: u64, - pub new_tree_fee_lamports: u64, -} - #[account] #[derive(Default)] pub struct ProgramApprovalV0 { @@ -149,14 +121,3 @@ pub const MOBILE_HOTSPOT_INFO_SIZE: usize = 8 + 1 + // is full hotspot 2 + // num location assers 60; // pad - -#[macro_export] -macro_rules! data_only_config_seeds { - ( $data_only_config:expr ) => { - &[ - "data_only_config".as_bytes(), - $data_only_config.dao.as_ref(), - &[$data_only_config.bump_seed], - ] - }; -} diff --git a/programs/helium-sub-daos/src/instructions/initialize_sub_dao_v0.rs b/programs/helium-sub-daos/src/instructions/initialize_sub_dao_v0.rs index 085a326d9..b06032822 100644 --- a/programs/helium-sub-daos/src/instructions/initialize_sub_dao_v0.rs +++ b/programs/helium-sub-daos/src/instructions/initialize_sub_dao_v0.rs @@ -57,7 +57,6 @@ pub struct InitializeSubDaoArgsV0 { pub dc_burn_authority: Pubkey, pub registrar: Pubkey, pub delegator_rewards_percent: u64, - pub onboarding_data_only_dc_fee: u64, } #[derive(Accounts)] @@ -282,7 +281,6 @@ pub fn handler(ctx: Context, args: InitializeSubDaoArgsV0) - vehnt_fall_rate: 0, delegator_pool: ctx.accounts.delegator_pool.key(), delegator_rewards_percent: args.delegator_rewards_percent, - onboarding_data_only_dc_fee: args.onboarding_data_only_dc_fee, }); resize_to_fit( diff --git a/programs/helium-sub-daos/src/instructions/update_sub_dao_v0.rs b/programs/helium-sub-daos/src/instructions/update_sub_dao_v0.rs index eedef4b30..ab660f219 100644 --- a/programs/helium-sub-daos/src/instructions/update_sub_dao_v0.rs +++ b/programs/helium-sub-daos/src/instructions/update_sub_dao_v0.rs @@ -11,7 +11,6 @@ pub struct UpdateSubDaoArgsV0 { pub active_device_aggregator: Option, pub registrar: Option, pub delegator_rewards_percent: Option, - pub onboarding_data_only_dc_fee: Option, } #[derive(Accounts)] @@ -55,10 +54,6 @@ pub fn handler(ctx: Context, args: UpdateSubDaoArgsV0) -> Result ctx.accounts.sub_dao.registrar = registrar; } - if let Some(onboarding_data_only_dc_fee) = args.onboarding_data_only_dc_fee { - ctx.accounts.sub_dao.onboarding_data_only_dc_fee = onboarding_data_only_dc_fee; - } - let max_percent = 100_u64.checked_mul(10_0000000).unwrap(); if let Some(delegator_rewards_percent) = args.delegator_rewards_percent { require_gte!(max_percent, delegator_rewards_percent); diff --git a/programs/helium-sub-daos/src/state.rs b/programs/helium-sub-daos/src/state.rs index 404fc7f36..4f302f360 100644 --- a/programs/helium-sub-daos/src/state.rs +++ b/programs/helium-sub-daos/src/state.rs @@ -191,5 +191,4 @@ pub struct SubDaoV0 { pub bump_seed: u8, pub registrar: Pubkey, // vsr registrar pub delegator_rewards_percent: u64, // number between 0-10,000. The % of DNT rewards delegators receive with 8 decimal places of accuracy - pub onboarding_data_only_dc_fee: u64, } diff --git a/tests/helium-entity-manager.ts b/tests/helium-entity-manager.ts index 634507ac0..759571f59 100644 --- a/tests/helium-entity-manager.ts +++ b/tests/helium-entity-manager.ts @@ -1,19 +1,16 @@ +import * as anchor from "@coral-xyz/anchor"; +import { Program } from "@coral-xyz/anchor"; import { Keypair as HeliumKeypair } from "@helium/crypto"; import { init as initDataCredits } from "@helium/data-credits-sdk"; import { init as initHeliumSubDaos } from "@helium/helium-sub-daos-sdk"; -import { getAssociatedTokenAddressSync } from "@solana/spl-token"; -import { Asset, AssetProof, createMintInstructions, sendInstructions, toBN, proofArgsAndAccounts } from "@helium/spl-utils"; -import { init as initPriceOracle } from "../packages/price-oracle-sdk/src"; -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { ComputeBudgetProgram, Keypair, PublicKey, LAMPORTS_PER_SOL, SystemProgram } from "@solana/web3.js"; +import { Asset, AssetProof, createMintInstructions, toBN } from "@helium/spl-utils"; import { AddGatewayV1 } from "@helium/transactions"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { ComputeBudgetProgram, Keypair, PublicKey } from "@solana/web3.js"; import chai from "chai"; import { - dataOnlyConfigKey, entityCreatorKey, init as initHeliumEntityManager, - iotInfoKey, onboardIotHotspot, onboardMobileHotspot, updateIotMetadata, @@ -51,9 +48,7 @@ import { import { BN } from "bn.js"; import chaiAsPromised from "chai-as-promised"; import { MerkleTree } from "../deps/solana-program-library/account-compression/sdk/src/merkle-tree"; -import { exists, loadKeypair } from "./utils/solana"; -import { getConcurrentMerkleTreeAccountSize, SPL_ACCOUNT_COMPRESSION_PROGRAM_ID } from "@solana/spl-account-compression"; -import { createMockCompression } from "./utils/compression"; +import { loadKeypair } from "./utils/solana"; chai.use(chaiAsPromised); @@ -141,230 +136,6 @@ describe("helium-entity-manager", () => { ); }); - it("initializes a data only config", async () => { - const [height, buffer, canopy] = [14, 64, 11]; - const merkle = Keypair.generate(); - const space = getConcurrentMerkleTreeAccountSize(height, buffer, canopy); - const cost = await provider.connection.getMinimumBalanceForRentExemption( - space - ); - await sendInstructions( - provider, - [ - SystemProgram.createAccount({ - fromPubkey: provider.wallet.publicKey, - newAccountPubkey: merkle.publicKey, - lamports: cost, - space: space, - programId: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, - }), - ], - [merkle] - ); - await hemProgram.methods.initializeDataOnlyV0({ - authority: me, - newTreeDepth: height, - newTreeBufferSize: buffer, - newTreeSpace: new BN(getConcurrentMerkleTreeAccountSize(height, buffer, canopy)), - newTreeFeeLamports: new BN((LAMPORTS_PER_SOL * 30) / 2**height), - name: "DATAONLY", - metadataUrl: "test", - }).accounts({ - dao, - merkleTree: merkle.publicKey, - }).preInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 350000 }), - ]).rpc(); - }); - - describe("with data only config", () => { - let ecc: string; - let rewardableEntityConfig: PublicKey; - let getAssetFn: ( - url: string, - assetId: PublicKey - ) => Promise; - let getAssetProofFn: ( - url: string, - assetId: PublicKey - ) => Promise; - let hotspotOwner = Keypair.generate(); - let hotspot: PublicKey; - let startDcBal = DC_FEE * 10; - - beforeEach(async () => { - ({ rewardableEntityConfig } = await initTestRewardableEntityConfig( - hemProgram, - subDao, - )); - ecc = (await HeliumKeypair.makeRandom()).address.b58; - const [height, buffer, canopy] = [3, 8, 0]; - const merkle = Keypair.generate(); - const space = getConcurrentMerkleTreeAccountSize(height, buffer, canopy); - const cost = await provider.connection.getMinimumBalanceForRentExemption( - space - ); - await sendInstructions( - provider, - [ - SystemProgram.createAccount({ - fromPubkey: provider.wallet.publicKey, - newAccountPubkey: merkle.publicKey, - lamports: cost, - space: space, - programId: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, - }), - ], - [merkle] - ); - await hemProgram.methods.initializeDataOnlyV0({ - authority: me, - newTreeDepth: height, - newTreeBufferSize: buffer, - newTreeSpace: new BN(getConcurrentMerkleTreeAccountSize(height, buffer, canopy)), - newTreeFeeLamports: new BN((LAMPORTS_PER_SOL * 30) / 2**height), - name: "DATAONLY", - metadataUrl: "test", - }).accounts({ - dao, - merkleTree: merkle.publicKey, - }).preInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 350000 }), - ]).rpc({skipPreflight: true}); - - const doAcc = await hemProgram.account.dataOnlyConfigV0.fetch(dataOnlyConfigKey(dao)[0]); - - ({getAssetFn, getAssetProofFn, hotspot} = await createMockCompression({ - collection: doAcc.collection, - dao, - merkle: merkle.publicKey, - ecc, - hotspotOwner, - })); - - await dcProgram.methods - .mintDataCreditsV0({ - hntAmount: toBN(startDcBal, 8), - dcAmount: null, - }) - .accounts({ dcMint }) - .rpc({ skipPreflight: true }); - }); - it("issues and onboards a data only hotspot", async () => { - let hotspotOwner = Keypair.generate(); - const issueMethod = hemProgram.methods - .issueDataOnlyEntityV0({ - entityKey: Buffer.from(bs58.decode(ecc)), - }) - .preInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 500000 }), - ]) - .accounts({ - recipient: hotspotOwner.publicKey, - dao, - eccVerifier: eccVerifier.publicKey, - }) - .signers([eccVerifier]); - - const { keyToAsset } = await issueMethod.pubkeys(); - await issueMethod.rpc({ skipPreflight: true }); - - console.log(keyToAsset?.toString()); - const ktaAcc = await hemProgram.account.keyToAssetV0.fetch( - keyToAsset! - ); - expect(Boolean(ktaAcc)).to.be.true; - expect(ktaAcc.asset.toString()).to.eq(hotspot.toString()); - expect(ktaAcc.dao.toString()).to.eq(dao.toString()); - - const { - args, - } = await proofArgsAndAccounts({ - connection: hemProgram.provider.connection, - assetId: hotspot, - getAssetFn, - getAssetProofFn, - }); - const onboardMethod = hemProgram.methods - .onboardDataOnlyIotHotspotV0({ - ...args, - location: null, - elevation: 50, - gain: 100, - }).accounts({ - rewardableEntityConfig, - hotspotOwner: hotspotOwner.publicKey, - keyToAsset, - iotInfo: iotInfoKey(rewardableEntityConfig, ecc)[0], - subDao, - }).signers([hotspotOwner]); - - const { iotInfo } = await onboardMethod.pubkeys(); - await onboardMethod.rpc(); - - const iotInfoAccount = await hemProgram.account.iotHotspotInfoV0.fetch( - iotInfo! - ); - expect(Boolean(iotInfoAccount)).to.be.true; - expect(iotInfoAccount.asset.toString()).to.eq(hotspot.toString()); - expect(iotInfoAccount.location).to.be.null; - expect(iotInfoAccount.elevation).to.eq(50); - expect(iotInfoAccount.gain).to.eq(100); - expect(iotInfoAccount.isFullHotspot).to.be.false; - - }); - - it("can swap tree when it's full", async () => { - let hotspotOwner = Keypair.generate(); - - // fill up the tree - while (true) { - try { - ecc = (await HeliumKeypair.makeRandom()).address.b58; - await hemProgram.methods - .issueDataOnlyEntityV0({ - entityKey: Buffer.from(bs58.decode(ecc)), - }) - .preInstructions([ - ComputeBudgetProgram.setComputeUnitLimit({ units: 500000 }), - ]) - .accounts({ - recipient: hotspotOwner.publicKey, - dao, - eccVerifier: eccVerifier.publicKey, - }) - .signers([eccVerifier]).rpc({ skipPreflight: true }); - } catch (err) { - break; - } - } - const [height, buffer, canopy] = [3, 8, 0]; - const newMerkle = Keypair.generate(); - const space = getConcurrentMerkleTreeAccountSize(height, buffer, canopy); - const cost = await provider.connection.getMinimumBalanceForRentExemption( - space - ); - await sendInstructions( - provider, - [ - SystemProgram.createAccount({ - fromPubkey: provider.wallet.publicKey, - newAccountPubkey: newMerkle.publicKey, - lamports: cost, - space: space, - programId: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, - }), - ], - [newMerkle] - ); - await hemProgram.methods.updateDataOnlyTreeV0().accounts({ - dataOnlyConfig: dataOnlyConfigKey(dao)[0], - newMerkleTree: newMerkle.publicKey, - }).rpc({skipPreflight: true}); - }) - }); - - it("initializes a maker", async () => { const { rewardableEntityConfig } = await initTestRewardableEntityConfig( hemProgram, @@ -490,14 +261,87 @@ describe("helium-entity-manager", () => { maker = makerConf.maker; makerKeypair = makerConf.makerKeypair; + hotspotCollection = makerConf.collection; - ({getAssetFn, getAssetProofFn, hotspot} = await createMockCompression({ - collection: makerConf.collection, - dao, - merkle: makerConf.merkle, - ecc, - hotspotOwner, - })); + // Setup merkle tree -- this isn't needed anywhere but localnet, + // we're effectively duplicating metaplex digital asset api + const merkle = makerConf.merkle; + hotspot = await getLeafAssetId(merkle, new BN(0)); + + const leaves = Array(2 ** 3).fill(Buffer.alloc(32)); + + const creators = [ + { + address: entityCreatorKey(dao)[0], + verified: true, + share: 100, + }, + ]; + metadata = { + name: animalHash(ecc).replace(/\s/g, "-").toLowerCase().slice(0, 32), + symbol: "HOTSPOT", + uri: `https://entities.nft.helium.io/${ecc}`, + collection: { + key: hotspotCollection, + verified: true, + }, + creators, + sellerFeeBasisPoints: 0, + primarySaleHappened: true, + isMutable: true, + editionNonce: null, + tokenStandard: TokenStandard.NonFungible, + uses: null, + tokenProgramVersion: TokenProgramVersion.Original, + }; + const hash = computeCompressedNFTHash( + hotspot, + hotspotOwner.publicKey, + hotspotOwner.publicKey, + new anchor.BN(0), + metadata + ); + leaves[0] = hash; + merkleTree = new MerkleTree(leaves); + const proof = merkleTree.getProof(0); + getAssetFn = async () => + ({ + id: await getLeafAssetId(merkle, new BN(0)), + content: { + metadata: { + name: metadata.name, + symbol: metadata.symbol, + }, + json_uri: metadata.uri, + }, + royalty: { + basis_points: metadata.sellerFeeBasisPoints, + primary_sale_happened: true, + }, + mutable: true, + supply: { + edition_nonce: null, + }, + grouping: metadata.collection.key, + uses: metadata.uses, + creators: metadata.creators, + ownership: { owner: hotspotOwner.publicKey }, + compression: { + compressed: true, + eligible: true, + dataHash: computeDataHash(metadata), + creatorHash: computeCreatorHash(creators), + }, + } as Asset); + getAssetProofFn = async () => { + return { + root: new PublicKey(proof.root), + proof: proof.proof.map((p) => new PublicKey(p)), + nodeIndex: 0, + leaf: new PublicKey(proof.leaf), + treeId: merkle, + }; + }; }); // Only uncomment this when you want to debug the sig verifier service locally. @@ -710,13 +554,84 @@ describe("helium-entity-manager", () => { makerKeypair = makerConf.makerKeypair; hotspotCollection = makerConf.collection; - ({getAssetFn, getAssetProofFn, hotspot} = await createMockCompression({ - collection: makerConf.collection, - dao, - merkle: makerConf.merkle, - ecc, - hotspotOwner, - })); + // Setup merkle tree -- this isn't needed anywhere but localnet, + // we're effectively duplicating metaplex digital asset api + const merkle = makerConf.merkle; + hotspot = await getLeafAssetId(merkle, new BN(0)); + + const leaves = Array(2 ** 3).fill(Buffer.alloc(32)); + const creators = [ + { + address: entityCreatorKey(dao)[0], + verified: true, + share: 100, + }, + ]; + metadata = { + name: animalHash(ecc).replace(/\s/g, "-").toLowerCase().slice(0, 32), + symbol: "HOTSPOT", + uri: `https://entities.nft.helium.io/${ecc}`, + collection: { + key: hotspotCollection, + verified: true, + }, + creators, + sellerFeeBasisPoints: 0, + primarySaleHappened: true, + isMutable: true, + editionNonce: null, + tokenStandard: TokenStandard.NonFungible, + uses: null, + tokenProgramVersion: TokenProgramVersion.Original, + }; + const hash = computeCompressedNFTHash( + hotspot, + hotspotOwner.publicKey, + hotspotOwner.publicKey, + new anchor.BN(0), + metadata + ); + leaves[0] = hash; + merkleTree = new MerkleTree(leaves); + const proof = merkleTree.getProof(0); + getAssetFn = async () => + ({ + id: await getLeafAssetId(merkle, new BN(0)), + content: { + metadata: { + name: metadata.name, + symbol: metadata.symbol, + }, + json_uri: metadata.uri, + }, + royalty: { + basis_points: metadata.sellerFeeBasisPoints, + primary_sale_happened: true, + }, + mutable: true, + supply: { + edition_nonce: null, + }, + grouping: metadata.collection.key, + uses: metadata.uses, + creators: metadata.creators, + ownership: { owner: hotspotOwner.publicKey }, + compression: { + compressed: true, + eligible: true, + dataHash: computeDataHash(metadata), + creatorHash: computeCreatorHash(creators), + }, + } as Asset); + getAssetProofFn = async () => { + return { + root: new PublicKey(proof.root), + proof: proof.proof.map((p) => new PublicKey(p)), + nodeIndex: 0, + leaf: new PublicKey(proof.leaf), + treeId: merkle, + }; + }; }); it("issues an iot hotspot", async () => { diff --git a/tests/helium-sub-daos.ts b/tests/helium-sub-daos.ts index f82b3d523..679bab26c 100644 --- a/tests/helium-sub-daos.ts +++ b/tests/helium-sub-daos.ts @@ -264,7 +264,6 @@ describe("helium-sub-daos", () => { dcBurnAuthority: null, emissionSchedule: null, onboardingDcFee: null, - onboardingDataOnlyDcFee: null, activeDeviceAggregator: null, registrar: null, delegatorRewardsPercent: null, diff --git a/tests/utils/compression.ts b/tests/utils/compression.ts index ec8b700ab..cd8ca99e9 100644 --- a/tests/utils/compression.ts +++ b/tests/utils/compression.ts @@ -1,3 +1,4 @@ +import { resolveIndividual } from "@helium/spl-utils"; import { computeCompressedNFTHash, computeCreatorHash, @@ -5,7 +6,6 @@ import { getLeafAssetId, PROGRAM_ID as BUBBLEGUM_PROGRAM_ID, TokenProgramVersion, TokenStandard } from "@metaplex-foundation/mpl-bubblegum"; -import { Asset, AssetProof, resolveIndividual } from "@helium/spl-utils"; import { Metadata, PROGRAM_ID as TOKEN_METADATA_PROGRAM_ID } from "@metaplex-foundation/mpl-token-metadata"; import * as anchor from "@coral-xyz/anchor"; import { Program } from "@coral-xyz/anchor"; @@ -17,11 +17,6 @@ import { import { Keypair, PublicKey, SystemProgram } from "@solana/web3.js"; import { MerkleTree } from "../../deps/solana-program-library/account-compression/sdk/src/merkle-tree"; import { Bubblegum as MplBubblegum, IDL as BubblegumIdl } from "./bubblegum"; -import { entityCreatorKey } from "@helium/helium-entity-manager-sdk"; -// @ts-ignore -import animalHash from "angry-purple-tiger"; -import { BN } from "bn.js"; - export async function createCompressionNft({ provider, @@ -180,102 +175,3 @@ export async function createCompressionNft({ dataHash: computeDataHash(metadata), }; } - -// Setup merkle tree -- this isn't needed anywhere but localnet, -// we're effectively duplicating metaplex digital asset api -export async function createMockCompression({ - collection, - dao, - merkle, - ecc, - hotspotOwner = Keypair.generate(), -} : { - collection: PublicKey, - dao: PublicKey, - ecc: string, - merkle: PublicKey, - hotspotOwner: Keypair, -}) { - - const creators = [ - { - address: entityCreatorKey(dao)[0], - verified: true, - share: 100, - }, - ]; - let metadata: any = { - name: animalHash(ecc).replace(/\s/g, "-").toLowerCase().slice(0, 32), - symbol: "HOTSPOT", - uri: `https://entities.nft.helium.io/${ecc}`, - collection: { - key: collection, - verified: true, - }, - creators, - sellerFeeBasisPoints: 0, - primarySaleHappened: true, - isMutable: true, - editionNonce: null, - tokenStandard: TokenStandard.NonFungible, - uses: null, - tokenProgramVersion: TokenProgramVersion.Original, - }; - let hotspot = await getLeafAssetId(merkle, new BN(0)); - - const hash = computeCompressedNFTHash( - hotspot, - hotspotOwner.publicKey, - hotspotOwner.publicKey, - new anchor.BN(0), - metadata - ); - const leaves = Array(2 ** 3).fill(Buffer.alloc(32)); - leaves[0] = hash; - let merkleTree = new MerkleTree(leaves); - const proof = merkleTree.getProof(0); - let getAssetFn = async () => - ({ - id: await getLeafAssetId(merkle, new BN(0)), - content: { - metadata: { - name: metadata.name, - symbol: metadata.symbol, - }, - json_uri: metadata.uri, - }, - royalty: { - basis_points: metadata.sellerFeeBasisPoints, - primary_sale_happened: true, - }, - mutable: true, - supply: { - edition_nonce: null, - }, - grouping: metadata.collection.key, - uses: metadata.uses, - creators: metadata.creators, - ownership: { owner: hotspotOwner.publicKey }, - compression: { - compressed: true, - eligible: true, - dataHash: computeDataHash(metadata), - creatorHash: computeCreatorHash(creators), - }, - } as Asset); - let getAssetProofFn = async () => { - return { - root: new PublicKey(proof.root), - proof: proof.proof.map((p) => new PublicKey(p)), - nodeIndex: 0, - leaf: new PublicKey(proof.leaf), - treeId: merkle, - }; - }; - - return { - getAssetFn, - getAssetProofFn, - hotspot, - } -} \ No newline at end of file diff --git a/tests/utils/daos.ts b/tests/utils/daos.ts index af9c67bd6..c6576b359 100644 --- a/tests/utils/daos.ts +++ b/tests/utils/daos.ts @@ -110,7 +110,6 @@ export async function initTestSubdao( .initializeSubDaoV0({ registrar: registrar || Keypair.generate().publicKey, onboardingDcFee: toBN(DC_FEE, 0), - onboardingDataOnlyDcFee: toBN(DC_FEE / 4, 0), authority: authority, emissionSchedule: [ { diff --git a/utils/ecc-sig-verifier/src/main.rs b/utils/ecc-sig-verifier/src/main.rs index 98c6564b6..2f8837212 100644 --- a/utils/ecc-sig-verifier/src/main.rs +++ b/utils/ecc-sig-verifier/src/main.rs @@ -46,11 +46,6 @@ pub struct IssueEntityArgsV0 { pub entity_key: Vec, } -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)] -pub struct IssueDataOnlyEntityArgsV0 { - pub entity_key: Vec, -} - #[derive(Deserialize, Serialize, Default)] struct TransactionResponse { pub count: u32, @@ -112,30 +107,20 @@ async fn verify<'a>(verify: Json>) -> Result(verify: Json>) -> Result(verify: Json>) -> Result 0 { error!( "Cannot onboard hotspot with key: {:?}. It has not been migrated yet.", - pubkey.to_string() + keystr ); return Err(Status::BadRequest); }