Skip to content

Commit

Permalink
feat(#494): Implement HIP-96 wifi onboarding fees
Browse files Browse the repository at this point in the history
  • Loading branch information
ChewingGlass committed Dec 9, 2023
1 parent cff8984 commit 4fa7b6f
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 49 deletions.
3 changes: 3 additions & 0 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ address = "propFYxqmVcufMhk5esNMrexq2ogHbbC2kP9PU1qxKs" # Proposal
[[test.validator.clone]]
address = "66t3XARU6Ja3zj91gDZ2KoNLJHEMTYPSKqJWYb6PJJBA" # Proposal IDL

[[test.validator.clone]]
address = "moraMdsjyPFz8Lp1RJGoW4bQriSF5mHE7Evxt7hytSF" # Mobile price oracle

# Pyth price oracle
[[test.validator.clone]]
address = "7moA1i5vQUpfDwSpK6Pw9s56ahB7WFGidtbL2ujWrVvm"
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ circuit-breaker = { path = "./programs/circuit-breaker", features = ["cpi"] }
helium-sub-daos = { path = "./programs/helium-sub-daos", features = ["cpi"] }
helium-entity-manager = { path = "./programs/helium-entity-manager", features = ["cpi"] }
rewards-burn = { path = "./programs/rewards-burn", features = ["cpi"] }
price-oracle = { path = "./programs/price-oracle", features = ["cpi"] }
13 changes: 10 additions & 3 deletions packages/helium-admin-cli/src/create-subdao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
parseEmissionsSchedule,
sendInstructionsOrSquads,
} from './utils';
import { BN } from 'bn.js';

const SECS_PER_DAY = 86400;
const SECS_PER_YEAR = 365 * SECS_PER_DAY;
Expand Down Expand Up @@ -539,22 +540,28 @@ export async function run(args: any = process.argv) {
};
} else {
settings = {
mobileConfigV1: {
mobileConfigV2: {
feesByDevice: [
{
deviceType: { cbrs: {} },
dcOnboardingFee: toBN(40, 5),
locationStakingFee: toBN(10, 5),
mobileOnboardingFeeUsd: toBN(0, 6),
reserved: new Array(8).fill(new BN(0)),
},
{
deviceType: { wifiIndoor: {} },
dcOnboardingFee: toBN(0, 5),
dcOnboardingFee: toBN(10, 5),
locationStakingFee: toBN(0, 5),
mobileOnboardingFeeUsd: toBN(10, 6),
reserved: new Array(8).fill(new BN(0)),
},
{
deviceType: { wifiOutdoor: {} },
dcOnboardingFee: toBN(0, 5),
dcOnboardingFee: toBN(10, 5),
locationStakingFee: toBN(0, 5),
mobileOnboardingFeeUsd: toBN(20, 6),
reserved: new Array(8).fill(new BN(0)),
},
],
},
Expand Down
20 changes: 13 additions & 7 deletions packages/helium-admin-cli/src/update-rewardable-entity-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,22 +116,28 @@ export async function run(args: any = process.argv) {
};
} else {
settings = {
mobileConfigV1: {
mobileConfigV2: {
feesByDevice: [
{
deviceType: { cbrs: {} },
dcOnboardingFee: new BN(argv.cbrsDcOnboardingFee!),
locationStakingFee: new BN(argv.cbrsDcLocationStakingFee!),
dcOnboardingFee: toBN(40, 5),
locationStakingFee: toBN(10, 5),
mobileOnboardingFeeUsd: toBN(0, 6),
reserved: new Array(8).fill(new BN(0)),
},
{
deviceType: { wifiIndoor: {} },
dcOnboardingFee: new BN(argv.wifiDcOnboardingFee!),
locationStakingFee: new BN(argv.wifiDcLocationStakingFee!),
dcOnboardingFee: toBN(10, 5),
locationStakingFee: toBN(0, 5),
mobileOnboardingFeeUsd: toBN(10, 6),
reserved: new Array(8).fill(new BN(0)),
},
{
deviceType: { wifiOutdoor: {} },
dcOnboardingFee: new BN(argv.wifiDcOnboardingFee!),
locationStakingFee: new BN(argv.wifiDcLocationStakingFee!),
dcOnboardingFee: toBN(10, 5),
locationStakingFee: toBN(0, 5),
mobileOnboardingFeeUsd: toBN(20, 6),
reserved: new Array(8).fill(new BN(0)),
},
],
},
Expand Down
13 changes: 12 additions & 1 deletion packages/helium-entity-manager-sdk/src/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export const heliumEntityManagerResolvers = combineResolvers(
mint: "dntMint",
owner: "maker",
}),
resolveIndividual(async ({ path }) => {
if (path[path.length - 1] == "dntPrice") {
return new PublicKey("moraMdsjyPFz8Lp1RJGoW4bQriSF5mHE7Evxt7hytSF");
}
}),
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;
Expand Down Expand Up @@ -167,7 +172,13 @@ export const heliumEntityManagerResolvers = combineResolvers(
instruction: "issueBurnEntityV0",
mint: "mint",
account: "recipientAccount",
owner: "recipient"
owner: "recipient",
}),
ataResolver({
instruction: "onboardMobileHotspotV0",
mint: "dntMint",
account: "dntBurner",
owner: "payer",
}),
subDaoEpochInfoResolver
);
3 changes: 2 additions & 1 deletion programs/helium-entity-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ data-credits = { path = "../data-credits", features = ["cpi"] }
helium-sub-daos = { workspace = true }
solana-security-txt = { workspace = true }
default-env = { workspace = true }
rewards-burn = { workspace = true }
rewards-burn = { workspace = true }
price-oracle = { workspace = true }
2 changes: 2 additions & 0 deletions programs/helium-entity-manager/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ pub enum ErrorCode {
InvalidSymbol,
#[msg("Mobile device type not found")]
InvalidDeviceType,
#[msg("No mobile oracle price")]
NoOraclePrice,
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::error::ErrorCode;
use crate::state::*;
use anchor_lang::{prelude::*, solana_program::hash::hash};
use std::str::FromStr;

use anchor_spl::{
associated_token::AssociatedToken,
token::{Mint, Token},
token::{burn, Burn, Mint, Token, TokenAccount},
};
use data_credits::{
cpi::{
Expand All @@ -22,6 +23,7 @@ use helium_sub_daos::{

use account_compression_cpi::program::SplAccountCompression;
use bubblegum_cpi::get_asset_id;
use price_oracle::{calculate_current_price, PriceOracleV0};
use shared_utils::*;

#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
Expand Down Expand Up @@ -61,6 +63,12 @@ pub struct OnboardMobileHotspotV0<'info> {
/// CHECK: Only loaded if location is being asserted
#[account(mut)]
pub dc_burner: UncheckedAccount<'info>,
#[account(
mut,
associated_token::authority = payer,
associated_token::mint = dnt_mint
)]
pub dnt_burner: Account<'info, TokenAccount>,

#[account(
has_one = sub_dao,
Expand Down Expand Up @@ -92,10 +100,17 @@ pub struct OnboardMobileHotspotV0<'info> {
#[account(
mut,
has_one = dao,
has_one = dnt_mint,
)]
pub sub_dao: Box<Account<'info, SubDaoV0>>,
#[account(mut)]
pub dc_mint: Box<Account<'info, Mint>>,
#[account(mut)]
pub dnt_mint: Box<Account<'info, Mint>>,
#[account(
address = Pubkey::from_str("moraMdsjyPFz8Lp1RJGoW4bQriSF5mHE7Evxt7hytSF").unwrap()
)]
pub dnt_price: Box<Account<'info, PriceOracleV0>>,

#[account(
seeds=[
Expand Down Expand Up @@ -132,6 +147,16 @@ impl<'info> OnboardMobileHotspotV0<'info> {

CpiContext::new(self.data_credits_program.to_account_info(), cpi_accounts)
}

pub fn mobile_burn_ctx(&self) -> CpiContext<'_, '_, '_, 'info, Burn<'info>> {
let cpi_accounts = Burn {
mint: self.dnt_mint.to_account_info(),
from: self.dnt_burner.to_account_info(),
authority: self.payer.to_account_info(),
};

CpiContext::new(self.token_program.to_account_info(), cpi_accounts)
}
}

pub fn handler<'info>(
Expand Down Expand Up @@ -214,5 +239,15 @@ pub fn handler<'info>(
BurnWithoutTrackingArgsV0 { amount: dc_fee },
)?;

// Burn the mobile tokens
let dnt_fee = fees.mobile_onboarding_fee_usd;
let mobile_price = calculate_current_price(
&ctx.accounts.dnt_price.oracles,
Clock::get()?.unix_timestamp,
)
.ok_or_else(|| error!(ErrorCode::NoOraclePrice))?;
let mobile_fee = dnt_fee.checked_div(mobile_price).unwrap();
burn(ctx.accounts.mobile_burn_ctx(), mobile_fee)?;

Ok(())
}
50 changes: 42 additions & 8 deletions programs/helium-entity-manager/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ pub struct DeviceFeesV0 {
pub location_staking_fee: u64,
}

impl From<DeviceFeesV0> for DeviceFeesV1 {
fn from(value: DeviceFeesV0) -> Self {
DeviceFeesV1 {
device_type: value.device_type,
dc_onboarding_fee: value.dc_onboarding_fee,
location_staking_fee: value.location_staking_fee,
mobile_onboarding_fee_usd: 0,
reserved: [0_64; 8],
}
}
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy)]
pub struct DeviceFeesV1 {
pub device_type: MobileDeviceTypeV0,
pub dc_onboarding_fee: u64,
pub location_staking_fee: u64,
// mobile onboarding fee in usd with 6 decimals of precision
pub mobile_onboarding_fee_usd: u64,
pub reserved: [u64; 8],
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
#[allow(deprecated)]
pub enum ConfigSettingsV0 {
Expand All @@ -36,29 +58,40 @@ pub enum ConfigSettingsV0 {
full_location_staking_fee: u64,
dataonly_location_staking_fee: u64,
},
// Deprecated, use MobileConfigV1
// Deprecated, use MobileConfigV2
MobileConfig {
full_location_staking_fee: u64,
dataonly_location_staking_fee: u64,
},
// Deprecated, use MobileConfigV2
MobileConfigV1 {
fees_by_device: Vec<DeviceFeesV0>,
},
MobileConfigV2 {
fees_by_device: Vec<DeviceFeesV1>,
},
}

impl ConfigSettingsV0 {
#[allow(deprecated)]
pub fn mobile_device_fees(&self, device: MobileDeviceTypeV0) -> Option<DeviceFeesV0> {
pub fn mobile_device_fees(&self, device: MobileDeviceTypeV0) -> Option<DeviceFeesV1> {
match self {
ConfigSettingsV0::MobileConfig {
full_location_staking_fee,
..
} => Some(DeviceFeesV0 {
device_type: MobileDeviceTypeV0::Cbrs,
dc_onboarding_fee: 4000000_u64,
location_staking_fee: *full_location_staking_fee,
}),
} => Some(
DeviceFeesV0 {
device_type: MobileDeviceTypeV0::Cbrs,
dc_onboarding_fee: 4000000_u64,
location_staking_fee: *full_location_staking_fee,
}
.into(),
),
ConfigSettingsV0::MobileConfigV1 { fees_by_device, .. } => fees_by_device
.iter()
.find(|d| d.device_type == device)
.map(|i| i.clone().into()),
ConfigSettingsV0::MobileConfigV2 { fees_by_device, .. } => fees_by_device
.iter()
.find(|d| d.device_type == device)
.copied(),
Expand All @@ -79,7 +112,8 @@ impl ConfigSettingsV0 {
}
}
pub fn is_mobile(&self) -> bool {
matches!(self, ConfigSettingsV0::MobileConfigV1 { .. })
matches!(self, ConfigSettingsV0::MobileConfigV2 { .. })
|| matches!(self, ConfigSettingsV0::MobileConfigV1 { .. })
|| matches!(self, ConfigSettingsV0::MobileConfig { .. })
}

Expand Down
Loading

0 comments on commit 4fa7b6f

Please sign in to comment.