From e31ad9ce342759e56dbad56ad47122917eccb85c Mon Sep 17 00:00:00 2001 From: microwavedcola1 Date: Wed, 3 Aug 2022 10:25:09 +0200 Subject: [PATCH] token register trustless Signed-off-by: microwavedcola1 --- programs/mango-v4/src/instructions/mod.rs | 2 + .../src/instructions/token_register.rs | 2 +- .../instructions/token_register_trustless.rs | 133 ++++++++ programs/mango-v4/src/lib.rs | 9 + ts/client/src/client.ts | 35 +++ ts/client/src/mango_v4.ts | 284 ++++++++++++++++++ ts/client/src/scripts/example1-admin.ts | 86 ++++-- 7 files changed, 519 insertions(+), 32 deletions(-) create mode 100644 programs/mango-v4/src/instructions/token_register_trustless.rs diff --git a/programs/mango-v4/src/instructions/mod.rs b/programs/mango-v4/src/instructions/mod.rs index 856c88dd3..7d8dece86 100644 --- a/programs/mango-v4/src/instructions/mod.rs +++ b/programs/mango-v4/src/instructions/mod.rs @@ -37,6 +37,7 @@ pub use token_deposit::*; pub use token_deregister::*; pub use token_edit::*; pub use token_register::*; +pub use token_register_trustless::*; pub use token_update_index_and_rate::*; pub use token_withdraw::*; @@ -79,5 +80,6 @@ mod token_deposit; mod token_deregister; mod token_edit; mod token_register; +mod token_register_trustless; mod token_update_index_and_rate; mod token_withdraw; diff --git a/programs/mango-v4/src/instructions/token_register.rs b/programs/mango-v4/src/instructions/token_register.rs index c1908ca5b..80619e70b 100644 --- a/programs/mango-v4/src/instructions/token_register.rs +++ b/programs/mango-v4/src/instructions/token_register.rs @@ -61,7 +61,7 @@ pub struct TokenRegister<'info> { pub rent: Sysvar<'info, Rent>, } -#[derive(AnchorSerialize, AnchorDeserialize, Default)] +#[derive(AnchorSerialize, AnchorDeserialize)] pub struct InterestRateParams { pub util0: f32, pub rate0: f32, diff --git a/programs/mango-v4/src/instructions/token_register_trustless.rs b/programs/mango-v4/src/instructions/token_register_trustless.rs new file mode 100644 index 000000000..78b683db1 --- /dev/null +++ b/programs/mango-v4/src/instructions/token_register_trustless.rs @@ -0,0 +1,133 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{Mint, Token, TokenAccount}; +use fixed::types::I80F48; +use fixed_macro::types::I80F48; + +use crate::error::*; +use crate::instructions::{InterestRateParams, INDEX_START}; +use crate::state::*; +use crate::util::fill16_from_str; + +#[derive(Accounts)] +#[instruction(token_index: TokenIndex, bank_num: u32)] +pub struct TokenRegisterTrustless<'info> { + #[account( + has_one = fast_listing_admin, + )] + pub group: AccountLoader<'info, Group>, + pub fast_listing_admin: Signer<'info>, + + pub mint: Account<'info, Mint>, + + #[account( + init, + // using the token_index in this seed guards against reusing it + seeds = [group.key().as_ref(), b"Bank".as_ref(), &token_index.to_le_bytes(), &bank_num.to_le_bytes()], + bump, + payer = payer, + space = 8 + std::mem::size_of::(), + )] + pub bank: AccountLoader<'info, Bank>, + + #[account( + init, + seeds = [group.key().as_ref(), b"Vault".as_ref(), &token_index.to_le_bytes(), &bank_num.to_le_bytes()], + bump, + token::authority = group, + token::mint = mint, + payer = payer + )] + pub vault: Account<'info, TokenAccount>, + + #[account( + init, + // using the mint in this seed guards against registering the same mint twice + seeds = [group.key().as_ref(), b"MintInfo".as_ref(), mint.key().as_ref()], + bump, + payer = payer, + space = 8 + std::mem::size_of::(), + )] + pub mint_info: AccountLoader<'info, MintInfo>, + + /// CHECK: The oracle can be one of several different account types + pub oracle: UncheckedAccount<'info>, + + #[account(mut)] + pub payer: Signer<'info>, + + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, +} + +#[allow(clippy::too_many_arguments)] +pub fn token_register_trustless( + ctx: Context, + token_index: TokenIndex, + bank_num: u32, + name: String, +) -> Result<()> { + require_eq!(bank_num, 0); + require_neq!(token_index, 0); + + let mut bank = ctx.accounts.bank.load_init()?; + *bank = Bank { + group: ctx.accounts.group.key(), + name: fill16_from_str(name)?, + mint: ctx.accounts.mint.key(), + vault: ctx.accounts.vault.key(), + oracle: ctx.accounts.oracle.key(), + oracle_config: OracleConfig { + conf_filter: I80F48::from_num(0.10), + }, + deposit_index: INDEX_START, + borrow_index: INDEX_START, + cached_indexed_total_deposits: I80F48::ZERO, + cached_indexed_total_borrows: I80F48::ZERO, + indexed_deposits: I80F48::ZERO, + indexed_borrows: I80F48::ZERO, + index_last_updated: Clock::get()?.unix_timestamp, + bank_rate_last_updated: Clock::get()?.unix_timestamp, + avg_utilization: I80F48::ZERO, + adjustment_factor: I80F48::from_num(0.001), + util0: I80F48::from_num(0.6), + rate0: I80F48::from_num(0.15), + util1: I80F48::from_num(0.8), + rate1: I80F48::from_num(0.95), + max_rate: I80F48::from_num(3.0), + collected_fees_native: I80F48::ZERO, + loan_origination_fee_rate: I80F48::from_num(0.005), + loan_fee_rate: I80F48::from_num(0.005), + maint_asset_weight: I80F48::from_num(0), + init_asset_weight: I80F48::from_num(0), + maint_liab_weight: I80F48::from_num(1.4), + init_liab_weight: I80F48::from_num(1.5), + liquidation_fee: I80F48::from_num(0.2), + dust: I80F48::ZERO, + flash_loan_vault_initial: u64::MAX, + flash_loan_approved_amount: 0, + token_index, + bump: *ctx.bumps.get("bank").ok_or(MangoError::SomeError)?, + mint_decimals: ctx.accounts.mint.decimals, + bank_num: 0, + reserved: [0; 256], + }; + + let mut mint_info = ctx.accounts.mint_info.load_init()?; + *mint_info = MintInfo { + group: ctx.accounts.group.key(), + token_index, + padding1: Default::default(), + mint: ctx.accounts.mint.key(), + banks: Default::default(), + vaults: Default::default(), + oracle: ctx.accounts.oracle.key(), + registration_time: Clock::get()?.unix_timestamp, + reserved: [0; 256], + }; + + mint_info.banks[0] = ctx.accounts.bank.key(); + mint_info.vaults[0] = ctx.accounts.vault.key(); + + Ok(()) +} diff --git a/programs/mango-v4/src/lib.rs b/programs/mango-v4/src/lib.rs index 132410f84..68e01ae8f 100644 --- a/programs/mango-v4/src/lib.rs +++ b/programs/mango-v4/src/lib.rs @@ -86,6 +86,15 @@ pub mod mango_v4 { ) } + pub fn token_register_trustless( + ctx: Context, + token_index: TokenIndex, + bank_num: u32, + name: String, + ) -> Result<()> { + instructions::token_register_trustless(ctx, token_index, bank_num, name) + } + #[allow(clippy::too_many_arguments)] pub fn token_edit( ctx: Context, diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 83fbcec76..7b1c910b0 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -88,6 +88,20 @@ export class MangoClient { .rpc(); } + public async groupEdit( + group: Group, + newAdmin: PublicKey, + newFastListingAdmin: PublicKey, + ): Promise { + return await this.program.methods + .groupEdit(newAdmin, newFastListingAdmin) + .accounts({ + group: group.publicKey, + admin: (this.program.provider as AnchorProvider).wallet.publicKey, + }) + .rpc(); + } + public async groupClose(group: Group): Promise { const adminPk = (this.program.provider as AnchorProvider).wallet.publicKey; return await this.program.methods @@ -193,6 +207,27 @@ export class MangoClient { .rpc(); } + public async tokenRegisterTrustless( + group: Group, + mintPk: PublicKey, + oraclePk: PublicKey, + tokenIndex: number, + name: string, + ): Promise { + return await this.program.methods + .tokenRegisterTrustless(tokenIndex, 0, name) + .accounts({ + group: group.publicKey, + fastListingAdmin: (this.program.provider as AnchorProvider).wallet + .publicKey, + mint: mintPk, + oracle: oraclePk, + payer: (this.program.provider as AnchorProvider).wallet.publicKey, + rent: SYSVAR_RENT_PUBKEY, + }) + .rpc(); + } + public async tokenEdit( group: Group, tokenName: string, diff --git a/ts/client/src/mango_v4.ts b/ts/client/src/mango_v4.ts index 9e706f67f..b20cd843c 100644 --- a/ts/client/src/mango_v4.ts +++ b/ts/client/src/mango_v4.ts @@ -332,6 +332,148 @@ export type MangoV4 = { } ] }, + { + "name": "tokenRegisterTrustless", + "accounts": [ + { + "name": "group", + "isMut": false, + "isSigner": false + }, + { + "name": "fastListingAdmin", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "bank", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "path": "group" + }, + { + "kind": "const", + "type": "string", + "value": "Bank" + }, + { + "kind": "arg", + "type": "u16", + "path": "token_index" + }, + { + "kind": "arg", + "type": "u32", + "path": "bank_num" + } + ] + } + }, + { + "name": "vault", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "path": "group" + }, + { + "kind": "const", + "type": "string", + "value": "Vault" + }, + { + "kind": "arg", + "type": "u16", + "path": "token_index" + }, + { + "kind": "arg", + "type": "u32", + "path": "bank_num" + } + ] + } + }, + { + "name": "mintInfo", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "path": "group" + }, + { + "kind": "const", + "type": "string", + "value": "MintInfo" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "mint" + } + ] + } + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "tokenIndex", + "type": "u16" + }, + { + "name": "bankNum", + "type": "u32" + }, + { + "name": "name", + "type": "string" + } + ] + }, { "name": "tokenEdit", "accounts": [ @@ -5198,6 +5340,148 @@ export const IDL: MangoV4 = { } ] }, + { + "name": "tokenRegisterTrustless", + "accounts": [ + { + "name": "group", + "isMut": false, + "isSigner": false + }, + { + "name": "fastListingAdmin", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "bank", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "path": "group" + }, + { + "kind": "const", + "type": "string", + "value": "Bank" + }, + { + "kind": "arg", + "type": "u16", + "path": "token_index" + }, + { + "kind": "arg", + "type": "u32", + "path": "bank_num" + } + ] + } + }, + { + "name": "vault", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "path": "group" + }, + { + "kind": "const", + "type": "string", + "value": "Vault" + }, + { + "kind": "arg", + "type": "u16", + "path": "token_index" + }, + { + "kind": "arg", + "type": "u32", + "path": "bank_num" + } + ] + } + }, + { + "name": "mintInfo", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "path": "group" + }, + { + "kind": "const", + "type": "string", + "value": "MintInfo" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "mint" + } + ] + } + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "tokenIndex", + "type": "u16" + }, + { + "name": "bankNum", + "type": "u32" + }, + { + "name": "name", + "type": "string" + } + ] + }, { "name": "tokenEdit", "accounts": [ diff --git a/ts/client/src/scripts/example1-admin.ts b/ts/client/src/scripts/example1-admin.ts index f9267f5be..1fb88f35a 100644 --- a/ts/client/src/scripts/example1-admin.ts +++ b/ts/client/src/scripts/example1-admin.ts @@ -23,11 +23,13 @@ const DEVNET_MINTS = new Map([ ['BTC', '3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU'], ['SOL', 'So11111111111111111111111111111111111111112'], ['ORCA', 'orcarKHSqC5CDDsGbho8GKvwExejWHxTqGzXgcewB9L'], + ['MNGO', 'Bb9bsTQa1bGEtQ5KagGkvSHyuLqDWumFUcRqFusFNJWC'], ]); const DEVNET_ORACLES = new Map([ ['BTC', 'HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J'], ['SOL', 'J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix'], ['ORCA', 'A1WttWF7X3Rg6ZRpB2YQUFHCRh1kiXV8sKKLV3S9neJV'], + ['MNGO', '8k7F9Xb36oFJsjpCKpsXvg4cgBRoZtwNTc3EzG5Ttd2o'], ]); const GROUP_NUM = Number(process.env.GROUP_NUM || 0); @@ -64,37 +66,6 @@ async function main() { const group = await client.getGroupForAdmin(admin.publicKey, GROUP_NUM); console.log(`...registered group ${group.publicKey}`); - // register token 1 - console.log(`Registering BTC...`); - const btcDevnetMint = new PublicKey(DEVNET_MINTS.get('BTC')!); - const btcDevnetOracle = new PublicKey(DEVNET_ORACLES.get('BTC')!); - try { - await client.tokenRegister( - group, - btcDevnetMint, - btcDevnetOracle, - 0.1, - 1, // tokenIndex - 'BTC', - 0.01, - 0.4, - 0.07, - 0.8, - 0.9, - 0.88, - 0.0005, - 0.0005, - 0.8, - 0.6, - 1.2, - 1.4, - 0.02, - ); - await group.reloadAll(client); - } catch (error) { - console.log(error); - } - // stub oracle + register token 0 console.log(`Registering USDC...`); const usdcDevnetMint = new PublicKey(DEVNET_MINTS.get('USDC')!); @@ -132,6 +103,37 @@ async function main() { await group.reloadAll(client); } catch (error) {} + // register token 1 + console.log(`Registering BTC...`); + const btcDevnetMint = new PublicKey(DEVNET_MINTS.get('BTC')!); + const btcDevnetOracle = new PublicKey(DEVNET_ORACLES.get('BTC')!); + try { + await client.tokenRegister( + group, + btcDevnetMint, + btcDevnetOracle, + 0.1, + 1, // tokenIndex + 'BTC', + 0.01, + 0.4, + 0.07, + 0.8, + 0.9, + 0.88, + 0.0005, + 0.0005, + 0.8, + 0.6, + 1.2, + 1.4, + 0.02, + ); + await group.reloadAll(client); + } catch (error) { + console.log(error); + } + // register token 2 console.log(`Registering SOL...`); const solDevnetMint = new PublicKey(DEVNET_MINTS.get('SOL')!); @@ -194,11 +196,33 @@ async function main() { console.log(error); } + // register token 4 + console.log( + `Editing group, setting existing admin as fastListingAdmin to be able to add MNGO truslessly...`, + ); + await client.groupEdit(group, group.admin, group.admin); + console.log(`Registering MNGO...`); + const mngoDevnetMint = new PublicKey(DEVNET_MINTS.get('MNGO')!); + const mngoDevnetOracle = new PublicKey(DEVNET_ORACLES.get('MNGO')!); + try { + await client.tokenRegisterTrustless( + group, + mngoDevnetMint, + mngoDevnetOracle, + 4, + 'MNGO', + ); + await group.reloadAll(client); + } catch (error) { + console.log(error); + } + // log tokens/banks for (const bank of await group.banksMap.values()) { console.log( `...registered Bank ${bank.tokenIndex} ${bank.publicKey}, mint ${bank.mint}, oracle ${bank.oracle}`, ); + console.log(bank.toString()); } // register serum market