Skip to content
Draft
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
13 changes: 1 addition & 12 deletions examples/oft-solana/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,24 +100,13 @@ anchor keys sync

:warning: `--force` flag overwrites the existing keys with the ones you generate.

Run `anchor keys list` to view the generated programIds (public keys). The output should look something like this:
Optionally, run `anchor keys list` to view the generated programIds (public keys). The output should look something like this:

```
endpoint: <ENDPOINT_PROGRAM_ID>
oft: <OFT_PROGRAM_ID>
```

Copy the OFT's programId and go into [lib.rs](./programs/oft/src/lib.rs). Note the following snippet:

```
declare_id!(Pubkey::new_from_array(program_id_from_env!(
"OFT_ID",
"9UovNrJD8pQyBLheeHNayuG1wJSEAoxkmM14vw5gcsTT"
)));
```

Replace `9UovNrJD8pQyBLheeHNayuG1wJSEAoxkmM14vw5gcsTT` with the programId that you have copied.

### Building and Deploying the Solana OFT Program

Ensure you have Docker running before running the build command.
Expand Down
2 changes: 2 additions & 0 deletions examples/oft-solana/programs/oft/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod lz_receive;
pub mod lz_receive_types;
pub mod quote_oft;
pub mod quote_send;
pub mod renounce_freeze;
pub mod send;
pub mod set_oft_config;
pub mod set_pause;
Expand All @@ -14,6 +15,7 @@ pub use lz_receive::*;
pub use lz_receive_types::*;
pub use quote_oft::*;
pub use quote_send::*;
pub use renounce_freeze::*;
pub use send::*;
pub use set_oft_config::*;
pub use set_pause::*;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use crate::*;
use anchor_lang::solana_program;
use anchor_spl::{
token_2022::{
spl_token_2022::instruction::AuthorityType,
spl_token_2022::{self, solana_program::program_option::COption},
},
token_interface::{Mint, TokenInterface},
};

#[derive(Accounts)]
#[instruction()]
pub struct RenounceFreezeAuthority<'info> {
pub admin: Signer<'info>,
#[account(
seeds = [OFT_SEED, oft_store.token_escrow.as_ref()],
bump = oft_store.bump,
has_one = admin @OFTError::Unauthorized
)]
pub oft_store: Account<'info, OFTStore>,
#[account(
mut,
constraint = token_mint.freeze_authority.is_some() @CustomError::NoFreezeAuthority,
address = oft_store.token_mint,
mint::token_program = token_program
)]
pub token_mint: InterfaceAccount<'info, Mint>,
#[account(
constraint = token_mint.freeze_authority == COption::Some(current_authority.key()) @CustomError::InvalidFreezeAuthority
)]
pub current_authority: AccountInfo<'info>,
pub token_program: Interface<'info, TokenInterface>,
}

//
impl RenounceFreezeAuthority<'_> {
pub fn apply(ctx: Context<RenounceFreezeAuthority>) -> Result<()> {
let oft_store_seed = ctx.accounts.oft_store.token_escrow.key();
let seeds: &[&[u8]] = &[
OFT_SEED,
oft_store_seed.as_ref(),
&[ctx.accounts.oft_store.bump],
];

// Create the set_authority instruction directly
let ix = spl_token_2022::instruction::set_authority(
ctx.accounts.token_program.key,
&ctx.accounts.token_mint.key(),
None, // new authority
AuthorityType::FreezeAccount,
&ctx.accounts.current_authority.key(),
&[&ctx.accounts.oft_store.key()],
)?;

// Invoke with signing
solana_program::program::invoke_signed(
&ix,
&[
ctx.accounts.token_mint.to_account_info(),
ctx.accounts.current_authority.to_account_info(),
ctx.accounts.oft_store.to_account_info(),
],
&[&seeds],
)?;

Ok(())
}
}

#[error_code]
pub enum CustomError {
#[msg("No freeze authority exists on this mint.")]
NoFreezeAuthority,
#[msg("The provided authority does not match the freeze authority.")]
InvalidFreezeAuthority,
}
10 changes: 5 additions & 5 deletions examples/oft-solana/programs/oft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,9 @@ use oapp::{
endpoint::{MessagingFee, MessagingReceipt},
LzReceiveParams,
};
use solana_helper::program_id_from_env;
use state::*;

declare_id!(Pubkey::new_from_array(program_id_from_env!(
"OFT_ID",
"9UovNrJD8pQyBLheeHNayuG1wJSEAoxkmM14vw5gcsTT"
)));
declare_id!("9UovNrJD8pQyBLheeHNayuG1wJSEAoxkmM14vw5gcsTT");

pub const OFT_SEED: &[u8] = b"OFT";
pub const PEER_SEED: &[u8] = b"Peer";
Expand Down Expand Up @@ -62,6 +58,10 @@ pub mod oft {
WithdrawFee::apply(&mut ctx, &params)
}

pub fn renounce_freeze(ctx: Context<RenounceFreezeAuthority>) -> Result<()> {
RenounceFreezeAuthority::apply(ctx)
}

// ============================== Public ==============================

pub fn quote_oft(ctx: Context<QuoteOFT>, params: QuoteOFTParams) -> Result<QuoteOFTResult> {
Expand Down
1 change: 1 addition & 0 deletions examples/oft-solana/tasks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import './solana/createOFT'
import './solana/createOFTAdapter'
import './solana/sendOFT'
import './solana/setAuthority'
import './solana/renounceFreeze'
import './solana/getPrioFees'
import './solana/base58'
import './solana/setInboundRateLimit'
Expand Down
4 changes: 3 additions & 1 deletion examples/oft-solana/tasks/solana/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from '@metaplex-foundation/umi'
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
import { createWeb3JsEddsa } from '@metaplex-foundation/umi-eddsa-web3js'
import { toWeb3JsInstruction, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters'
import { toWeb3JsInstruction, toWeb3JsKeypair, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters'
import { AddressLookupTableAccount, Connection } from '@solana/web3.js'
import { getSimulationComputeUnits } from '@solana-developers/helpers'
import bs58 from 'bs58'
Expand Down Expand Up @@ -63,12 +63,14 @@ export const deriveConnection = async (eid: EndpointId) => {
const umi = createUmi(connection.rpcEndpoint).use(mplToolbox())
const umiWalletKeyPair = umi.eddsa.createKeypairFromSecretKey(bs58.decode(privateKey))
const umiWalletSigner = createSignerFromKeypair(umi, umiWalletKeyPair)
const web3JsKeypair = toWeb3JsKeypair(umiWalletKeyPair)
umi.use(signerIdentity(umiWalletSigner))
return {
connection,
umi,
umiWalletKeyPair,
umiWalletSigner,
web3JsKeypair,
}
}

Expand Down
76 changes: 76 additions & 0 deletions examples/oft-solana/tasks/solana/renounceFreeze.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { AnchorProvider, Program } from '@coral-xyz/anchor'
import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet'
import { TOKEN_PROGRAM_ID, getMint } from '@solana/spl-token'
import { task } from 'hardhat/config'

import { types } from '@layerzerolabs/devtools-evm-hardhat'
import { EndpointId } from '@layerzerolabs/lz-definitions'

import { deriveConnection, getExplorerTxLink } from './index'

interface Args {
eid: EndpointId
programId: string
oftStore: string
computeUnitPriceScaleFactor: number
}

// TODO: no need to pass in oft store? since we can derive
task('lz:oft:solana:renounce-freeze', 'Renounce freeze authority for an OFT token')
.addParam('eid', 'The endpoint ID', undefined, types.eid)
.addParam('programId', 'The OFT program ID', undefined, types.string)
.addParam('oftStore', 'The OFT Store public key', undefined, types.string)
.addParam('computeUnitPriceScaleFactor', 'The compute unit price scale factor', 4, types.float, true)
.setAction(async ({ eid, programId: programIdStr, oftStore: oftStoreStr, computeUnitPriceScaleFactor }: Args) => {
const { connection, umiWalletSigner, web3JsKeypair } = await deriveConnection(eid)

// TODO: clean up below block
const wallet = new NodeWallet(web3JsKeypair)
const provider = new AnchorProvider(connection, wallet, {
commitment: 'processed',
})

const IDL = await import('../../target/idl/oft.json').then((module) => module.default)
const types = await import('../../target/types/oft').then((module) => module)

// @ts-expect-error we can ignore the IDL type error, which is a quick or Anchor
const program = new Program<typeof types.IDL>(IDL, programIdStr, provider)

const oftStoreAccount = await program.account.oftStore.fetch(oftStoreStr)
const tokenMint = oftStoreAccount.tokenMint

const mintAccount = await getMint(connection, tokenMint) // to support Token-2022, pass in program ID as third param here

// we assume that the mint authority is set to the multisig
const multisig = mintAccount.mintAuthority

if (!multisig) {
throw new Error('Mint authority is not set.')
}

const oftStoreAccountData = await program.account.oftStore.fetch(oftStoreStr)

const signerIsAdmin = umiWalletSigner.publicKey.toString() == oftStoreAccountData.admin.toString()
if (!signerIsAdmin) {
throw new Error('Your keypair is not the OFT Store admin.')
}

// Call the method
try {
const tx = await program.methods
.renounceFreeze() // Method name
.accounts({
signer: umiWalletSigner.publicKey,
oftStore: oftStoreStr,
tokenMint: tokenMint,
currentAuthority: multisig,
tokenProgram: TOKEN_PROGRAM_ID.toBase58(), // currently only supports SPL Token standard
})
.signers([web3JsKeypair])
.rpc()

console.log('Transaction successful:', getExplorerTxLink(tx, eid == EndpointId.SOLANA_V2_TESTNET))
} catch (err) {
console.error('Transaction failed:', err)
}
})
Loading