Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#494): Implement HIP-96 wifi onboarding fees #506

Merged
merged 2 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -28,4 +28,5 @@ shared-utils = { path = "./utils/shared-utils" }
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"] }
price-oracle = { path = "./programs/price-oracle", features = ["cpi"] }
no-emit = { path = "./programs/no-emit", 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: "issueNotEmittedEntityV0",
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 }
no-emit = { workspace = true }
price-oracle = { workspace = true }
no-emit = { 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_u64; 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).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
Loading