# OnRe Smart Contract Integration

### Quick Start

<table><thead><tr><th width="177.8306884765625">Property</th><th width="432.40875244140625">Value</th></tr></thead><tbody><tr><td>Program ID</td><td><code>onreuGhHHgVzMWSkj2oQDLDtvvGvoepBPkqyaubFcwe</code></td></tr><tr><td>Network</td><td>Solana Mainnet</td></tr><tr><td>Anchor Version</td><td>0.31.1</td></tr><tr><td>Github</td><td><a href="https://github.com/onre-finance/onre-sol">https://github.com/onre-finance/onre-sol</a></td></tr></tbody></table>

### Overview

The OnRe program enables token exchanges through **offers**. Each offer represents a trading pair (e.g., USDC → ONyc) with dynamic pricing based on time-based APR growth.

#### Key Concepts:

* **Offer**: A PDA account identified by a token pair `(token_in_mint, token_out_mint)`
* **Token In**: The token users pay (e.g., USDC, USDG)
* **Token Out**: The token users receive (e.g., ONyc)
* **Pricing Vectors**: Time-based price schedules with APR-driven growth

### Choosing Your Integration Path

`take_offer`  vs  `take_offer_permissionless`

| Feature                    | take\_offer                    | take\_offer\_permissionless                        |
| -------------------------- | ------------------------------ | -------------------------------------------------- |
| Token Routing              | Direct user-to-boss transfers  | Routes through program-owned intermediary accounts |
| Smart Contract Integration | Works well                     | Simpler setup (recommended)                        |
| Approval Message           | Optional (when offer requires) | Optional (when offer requires)                     |
| Pricing                    | Same                           | Same                                               |
| Fees                       | Same                           | Same                                               |
| Account Setup              | Fewer accounts needed          | More accounts, but program-controlled              |

#### Recommendation

Use `take_offer_permissionless` for most integrations:

* Simpler integration – no direct user-to-boss token account relationships needed
* Atomic routing through program-controlled PDAs
* Better for smart contract CPI calls

### PDA Seeds Reference

All PDAs are derived from the program ID: `onreuGhHHgVzMWSkj2oQDLDtvvGvoepBPkqyaubFcwe`

<table><thead><tr><th width="153.583984375">PDA</th><th width="361.16796875">Seed</th><th>Description</th></tr></thead><tbody><tr><td>State</td><td><code>"state"</code></td><td>Program state (contains boss, kill switch, approvers)</td></tr><tr><td>Offer</td><td><code>"offer" + token_in_mint + token_out_mint</code></td><td>Offer account for a token pair</td></tr><tr><td>Vault Authority</td><td><code>"offer_vault_authority"</code></td><td>Authority for vault token accounts</td></tr><tr><td>Permissionless Authority</td><td><code>"permissionless-1"</code></td><td>Authority for intermediary token routing</td></tr><tr><td>Mint Authority</td><td><code>"mint_authority"</code></td><td>Authority for mint operations</td></tr></tbody></table>

```typespec
import { PublicKey } from "@solana/web3.js";

const PROGRAM_ID = new PublicKey("onreuGhHHgVzMWSkj2oQDLDtvvGvoepBPkqyaubFcwe");

// All PDAs you'll need - compute these once at initialization
const pdas = {
  state: PublicKey.findProgramAddressSync(
    [Buffer.from("state")],
    PROGRAM_ID
  )[0],

  vaultAuthority: PublicKey.findProgramAddressSync(
    [Buffer.from("offer_vault_authority")],
    PROGRAM_ID
  )[0],

  permissionlessAuthority: PublicKey.findProgramAddressSync(
    [Buffer.from("permissionless-1")],
    PROGRAM_ID
  )[0],

  mintAuthority: PublicKey.findProgramAddressSync(
    [Buffer.from("mint_authority")],
    PROGRAM_ID
  )[0],
};

// Offer PDA for a specific token pair
function getOfferPda(tokenInMint: PublicKey, tokenOutMint: PublicKey): PublicKey {
  return PublicKey.findProgramAddressSync(
    [Buffer.from("offer"), tokenInMint.toBuffer(), tokenOutMint.toBuffer()],
    PROGRAM_ID
  )[0];
}
```

### Integration: `take_offer_permissionless`

This is the **recommended** integration path for most use cases.

#### Instruction Discriminator

`[37, 190, 224, 77, 197, 39, 203, 230]`

#### Arguments

| Name               | Type                      | Description                                             |
| ------------------ | ------------------------- | ------------------------------------------------------- |
| `token_in_amount`  | `u64`                     | Amount of token\_in the user is paying (including fees) |
| `approval_message` | `Option<ApprovalMessage>` | Pass `null` for permissionless offers                   |

#### Using Anchor Client (Recommended)

```typescript
import { Program, AnchorProvider, BN } from "@coral-xyz/anchor";
import { PublicKey, Keypair } from "@solana/web3.js";
import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token";
import { Onreapp } from "./types/onreapp";
import idl from "./idl/onreapp.json";

const PROGRAM_ID = new PublicKey("onreuGhHHgVzMWSkj2oQDLDtvvGvoepBPkqyaubFcwe");

// Pre-compute PDAs once at initialization
const pdas = {
  state: PublicKey.findProgramAddressSync([Buffer.from("state")], PROGRAM_ID)[0],
  vaultAuthority: PublicKey.findProgramAddressSync([Buffer.from("offer_vault_authority")], PROGRAM_ID)[0],
  permissionlessAuthority: PublicKey.findProgramAddressSync([Buffer.from("permissionless-1")], PROGRAM_ID)[0],
  mintAuthority: PublicKey.findProgramAddressSync([Buffer.from("mint_authority")], PROGRAM_ID)[0],
};

async function takeOfferPermissionless(
  program: Program<Onreapp>,
  tokenInMint: PublicKey,      // e.g., USDC mint
  tokenOutMint: PublicKey,     // e.g., ONyc mint
  tokenInAmount: number,       // Amount in base units (1 USDC = 1_000_000)
  user: Keypair,
  boss: PublicKey,             // Get from state account
  tokenInProgram: PublicKey = TOKEN_PROGRAM_ID,
  tokenOutProgram: PublicKey = TOKEN_PROGRAM_ID
) {
  const tx = await program.methods
    .takeOfferPermissionless(new BN(tokenInAmount), null)
    .accounts({
      tokenInMint,
      tokenOutMint,
      user: user.publicKey,
      tokenInProgram,
      tokenOutProgram,
      boss,
      vaultAuthority: pdas.vaultAuthority,
      permissionlessAuthority: pdas.permissionlessAuthority,
      mintAuthority: pdas.mintAuthority,
    })
    .signers([user])
    .rpc();

  console.log("Transaction signature:", tx);
  return tx;
}

// Usage Example
async function main() {
  const connection = new Connection("https://api.mainnet-beta.solana.com");
  const wallet = /* your wallet */;
  const provider = new AnchorProvider(connection, wallet, {});
  const program = new Program<Onreapp>(idl, provider);

  // Token mints
  const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
  const ONYC_MINT = new PublicKey("..."); // ONyc mint address

  // Get boss from state
  const state = await program.account.state.fetch(pdas.state);
  const boss = state.boss;

  // Execute: swap 100 USDC for ONyc
  await takeOfferPermissionless(
    program,
    USDC_MINT,
    ONYC_MINT,
    100_000_000, // 100 USDC (6 decimals)
    userKeypair,
    boss
  );
}
```

#### Building Transaction Manually (Low-Level)

Use this approach when building transactions without Anchor or for CPI.

```typescript
import {
  TransactionInstruction,
  PublicKey,
  SystemProgram,
  SYSVAR_INSTRUCTIONS_PUBKEY,
} from "@solana/web3.js";
import {
  TOKEN_PROGRAM_ID,
  ASSOCIATED_TOKEN_PROGRAM_ID,
  getAssociatedTokenAddressSync,
} from "@solana/spl-token";

const PROGRAM_ID = new PublicKey("onreuGhHHgVzMWSkj2oQDLDtvvGvoepBPkqyaubFcwe");
const DISCRIMINATOR = Buffer.from([37, 190, 224, 77, 197, 39, 203, 230]);

function buildTakeOfferPermissionlessInstruction(
  tokenInMint: PublicKey,
  tokenOutMint: PublicKey,
  tokenInAmount: bigint,
  user: PublicKey,
  boss: PublicKey,
  tokenInProgram: PublicKey = TOKEN_PROGRAM_ID,
  tokenOutProgram: PublicKey = TOKEN_PROGRAM_ID
): TransactionInstruction {
  // Derive PDAs
  const [statePda] = PublicKey.findProgramAddressSync(
    [Buffer.from("state")],
    PROGRAM_ID
  );
  const [offerPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("offer"), tokenInMint.toBuffer(), tokenOutMint.toBuffer()],
    PROGRAM_ID
  );
  const [vaultAuthorityPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("offer_vault_authority")],
    PROGRAM_ID
  );
  const [permissionlessAuthorityPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("permissionless-1")],
    PROGRAM_ID
  );
  const [mintAuthorityPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("mint_authority")],
    PROGRAM_ID
  );

  // Derive ATAs
  const vaultTokenInAccount = getAssociatedTokenAddressSync(
    tokenInMint, vaultAuthorityPda, true, tokenInProgram
  );
  const vaultTokenOutAccount = getAssociatedTokenAddressSync(
    tokenOutMint, vaultAuthorityPda, true, tokenOutProgram
  );
  const permissionlessTokenInAccount = getAssociatedTokenAddressSync(
    tokenInMint, permissionlessAuthorityPda, true, tokenInProgram
  );
  const permissionlessTokenOutAccount = getAssociatedTokenAddressSync(
    tokenOutMint, permissionlessAuthorityPda, true, tokenOutProgram
  );
  const userTokenInAccount = getAssociatedTokenAddressSync(
    tokenInMint, user, false, tokenInProgram
  );
  const userTokenOutAccount = getAssociatedTokenAddressSync(
    tokenOutMint, user, false, tokenOutProgram
  );
  const bossTokenInAccount = getAssociatedTokenAddressSync(
    tokenInMint, boss, false, tokenInProgram
  );

  // Build instruction data
  // Format: discriminator (8 bytes) + token_in_amount (8 bytes) + approval_message (1 byte for None)
  const data = Buffer.alloc(8 + 8 + 1);
  DISCRIMINATOR.copy(data, 0);
  data.writeBigUInt64LE(tokenInAmount, 8);
  data.writeUInt8(0, 16); // 0 = None for Option<ApprovalMessage>

  const keys = [
    { pubkey: offerPda, isSigner: false, isWritable: true },
    { pubkey: statePda, isSigner: false, isWritable: false },
    { pubkey: boss, isSigner: false, isWritable: false },
    { pubkey: vaultAuthorityPda, isSigner: false, isWritable: false },
    { pubkey: vaultTokenInAccount, isSigner: false, isWritable: true },
    { pubkey: vaultTokenOutAccount, isSigner: false, isWritable: true },
    { pubkey: permissionlessAuthorityPda, isSigner: false, isWritable: false },
    { pubkey: permissionlessTokenInAccount, isSigner: false, isWritable: true },
    { pubkey: permissionlessTokenOutAccount, isSigner: false, isWritable: true },
    { pubkey: tokenInMint, isSigner: false, isWritable: true },
    { pubkey: tokenInProgram, isSigner: false, isWritable: false },
    { pubkey: tokenOutMint, isSigner: false, isWritable: true },
    { pubkey: tokenOutProgram, isSigner: false, isWritable: false },
    { pubkey: userTokenInAccount, isSigner: false, isWritable: true },
    { pubkey: userTokenOutAccount, isSigner: false, isWritable: true },
    { pubkey: bossTokenInAccount, isSigner: false, isWritable: true },
    { pubkey: mintAuthorityPda, isSigner: false, isWritable: false },
    { pubkey: SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false },
    { pubkey: user, isSigner: true, isWritable: true },
    { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
    { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  ];

  return new TransactionInstruction({
    programId: PROGRAM_ID,
    keys,
    data,
  });
}
```

### Integration: `take_offer`

The direct flow requires fewer accounts but uses direct user-to-boss transfers.

#### Instruction Discriminator

`[137, 6, 172, 191, 222, 117, 178, 131]`

#### Using Anchor Client

```typescript
async function takeOffer(
  program: Program<Onreapp>,
  tokenInMint: PublicKey,
  tokenOutMint: PublicKey,
  tokenInAmount: number,
  user: Keypair,
  tokenInProgram: PublicKey = TOKEN_PROGRAM_ID,
  tokenOutProgram: PublicKey = TOKEN_PROGRAM_ID
) {
  // Anchor automatically resolves most accounts
  const tx = await program.methods
    .takeOffer(new BN(tokenInAmount), null)
    .accounts({
      tokenInMint,
      tokenOutMint,
      user: user.publicKey,
      tokenInProgram,
      tokenOutProgram,
    })
    .signers([user])
    .rpc();

  return tx;
}
```

### Fetching Market Data

#### Get Boss Address

```typescript
async function getBoss(program: Program<Onreapp>): Promise<PublicKey> {
  const [statePda] = PublicKey.findProgramAddressSync(
    [Buffer.from("state")],
    PROGRAM_ID
  );
  const state = await program.account.state.fetch(statePda);
  return state.boss;
}
```

#### Check Offer Configuration

```typescript
async function getOfferInfo(
  program: Program<Onreapp>,
  tokenInMint: PublicKey,
  tokenOutMint: PublicKey
) {
  const [offerPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("offer"), tokenInMint.toBuffer(), tokenOutMint.toBuffer()],
    PROGRAM_ID
  );

  const offer = await program.account.offer.fetch(offerPda);

  return {
    feeBasisPoints: offer.feeBasisPoints,     // Fee in basis points (100 = 1%)
    needsApproval: offer.needsApproval !== 0, // Whether offer requires approval
    allowPermissionless: offer.allowPermissionless !== 0,
    vectors: offer.vectors.filter(v => v.startTime !== 0), // Active pricing vectors
  };
}
```

#### Get Current NAV (Price)

```typescript
async function getCurrentNav(
  program: Program<Onreapp>,
  tokenInMint: PublicKey,
  tokenOutMint: PublicKey
): Promise<number> {
  const nav = await program.methods
    .getNav()
    .accounts({
      tokenInMint,
      tokenOutMint,
    })
    .view();

  // NAV is a u64 with 9 decimal precision
  // 1.0 = 1_000_000_000
  return Number(nav) / 1e9;
}
```

#### Get Current APY

```typescript
async function getCurrentApy(
  program: Program<Onreapp>,
  tokenInMint: PublicKey,
  tokenOutMint: PublicKey
): Promise<number> {
  const apy = await program.methods
    .getApy()
    .accounts({
      tokenInMint,
      tokenOutMint,
    })
    .view();

  // APY is scaled by 1_000_000 (1_000_000 = 1%)
  return Number(apy) / 1_000_000;
}
```

#### Calculate Expected Output

```typescript
function calculateExpectedOutput(
  tokenInAmount: number,    // Amount user wants to pay (base units)
  tokenInDecimals: number,  // e.g., 6 for USDC
  tokenOutDecimals: number, // e.g., 9 for ONyc
  currentNav: number,       // From getNav(), already divided by 1e9
  feeBasisPoints: number    // From offer account
): bigint {
  // Calculate fee
  const feeAmount = Math.floor(tokenInAmount * feeBasisPoints / 10000);
  const netAmount = tokenInAmount - feeAmount;

  // Calculate output
  // Formula: token_out = (net_amount * 10^(token_out_decimals + 9)) / (nav * 10^token_in_decimals)
  const navWithScale = BigInt(Math.floor(currentNav * 1e9));
  const tokenOutAmount =
    (BigInt(netAmount) * BigInt(10 ** (tokenOutDecimals + 9))) /
    (navWithScale * BigInt(10 ** tokenInDecimals));

  return tokenOutAmount;
}

// Example: Calculate how much ONyc for 100 USDC
const usdcAmount = 100_000_000; // 100 USDC (6 decimals)
const nav = await getCurrentNav(program, USDC_MINT, ONYC_MINT);
const offer = await getOfferInfo(program, USDC_MINT, ONYC_MINT);

const expectedOnyc = calculateExpectedOutput(
  usdcAmount,
  6,  // USDC decimals
  9,  // ONyc decimals
  nav,
  offer.feeBasisPoints
);

console.log(`Expected ONyc: ${Number(expectedOnyc) / 1e9}`);
```

### Pricing Model

The OnRe program uses a **discrete interval pricing model** with APR-based growth.

#### How Price is Calculated

1. Find Active Vector: The program finds the pricing vector with the latest `start_time` ≤ current time
2. Calculate Interval: `interval = floor((current_time - base_time) / price_fix_duration)`
3. Calculate Effective Time: `effective_time = (interval + 1) * price_fix_duration`
4. Calculate Price: `price = base_price * (1 + apr * effective_time / SECONDS_IN_YEAR)`

**Constants:**

* `base_price` has 9 decimal precision (1.0 = 1,000,000,000)
* `apr` is scaled by 1,000,000 (1% APR = 1,000,000)
* `SECONDS_IN_YEAR` = 31,536,000

#### Example Price Calculation

```typescript
const SECONDS_IN_YEAR = 31_536_000;
const APR_SCALE = 1_000_000;

function calculatePrice(
  basePrice: number,        // e.g., 1_000_000_000 for $1.00
  apr: number,              // e.g., 36_500 for 3.65% APR
  baseTime: number,         // Unix timestamp
  priceFixDuration: number, // e.g., 86400 for daily intervals
  currentTime: number       // Current Unix timestamp
): number {
  const elapsedSinceBase = currentTime - baseTime;
  const currentStep = Math.floor(elapsedSinceBase / priceFixDuration);
  const effectiveTime = (currentStep + 1) * priceFixDuration;

  // price = base_price * (1 + apr * effective_time / SECONDS_IN_YEAR)
  const factor = 1 + (apr / APR_SCALE) * (effectiveTime / SECONDS_IN_YEAR);
  return Math.floor(basePrice * factor);
}
```

### Error Handling

#### Common Error Codes

| Error                      | Code | Description                                 |
| -------------------------- | ---- | ------------------------------------------- |
| `NoActiveVector`           | 6001 | No pricing vector is active at current time |
| `KillSwitchActivated`      | 6002 | Program operations are halted               |
| `PermissionlessNotAllowed` | 6003 | Offer doesn't allow permissionless access   |
| `InvalidBoss`              | 6004 | Boss account doesn't match state            |
| `ApprovalRequired`         | 6005 | Offer requires approval message             |
| `InvalidTokenInMint`       | 6006 | Token in mint doesn't match offer           |
| `InvalidTokenOutMint`      | 6007 | Token out mint doesn't match offer          |
| `OverflowError`            | 6008 | Arithmetic overflow in calculations         |

#### Error Handling Example

```typescript
try {
  await takeOfferPermissionless(/* params */);
} catch (error) {
  if (error.message.includes("No active vector")) {
    console.error("No pricing vector is currently active. Check offer configuration.");
  } else if (error.message.includes("Kill switch is activated")) {
    console.error("Program is temporarily halted. Try again later.");
  } else if (error.message.includes("Permissionless take offer not allowed")) {
    console.error("This offer requires the direct take_offer instruction.");
  } else if (error.message.includes("insufficient funds")) {
    console.error("Insufficient token balance or vault liquidity.");
  }
  throw error;
}
```

### CPI from Rust Programs

For calling `take_offer_permissionless` from another Solana program via CPI:

```rust
use anchor_lang::prelude::*;

// OnRe program ID
pub const ONRE_PROGRAM_ID: Pubkey = pubkey!("onreuGhHHgVzMWSkj2oQDLDtvvGvoepBPkqyaubFcwe");

// Instruction discriminator for take_offer_permissionless
pub const TAKE_OFFER_PERMISSIONLESS_DISCRIMINATOR: [u8; 8] = [37, 190, 224, 77, 197, 39, 203, 230];

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct TakeOfferPermissionlessArgs {
    pub token_in_amount: u64,
    pub approval_message: Option<ApprovalMessage>,
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct ApprovalMessage {
    pub program_id: Pubkey,
    pub user_pubkey: Pubkey,
    pub expiry_unix: u64,
}

pub fn cpi_take_offer_permissionless<'info>(
    onre_program: AccountInfo<'info>,
    offer: AccountInfo<'info>,
    state: AccountInfo<'info>,
    boss: AccountInfo<'info>,
    vault_authority: AccountInfo<'info>,
    vault_token_in_account: AccountInfo<'info>,
    vault_token_out_account: AccountInfo<'info>,
    permissionless_authority: AccountInfo<'info>,
    permissionless_token_in_account: AccountInfo<'info>,
    permissionless_token_out_account: AccountInfo<'info>,
    token_in_mint: AccountInfo<'info>,
    token_in_program: AccountInfo<'info>,
    token_out_mint: AccountInfo<'info>,
    token_out_program: AccountInfo<'info>,
    user_token_in_account: AccountInfo<'info>,
    user_token_out_account: AccountInfo<'info>,
    boss_token_in_account: AccountInfo<'info>,
    mint_authority: AccountInfo<'info>,
    instructions_sysvar: AccountInfo<'info>,
    user: AccountInfo<'info>,
    associated_token_program: AccountInfo<'info>,
    system_program: AccountInfo<'info>,
    token_in_amount: u64,
    signer_seeds: Option<&[&[&[u8]]]>,
) -> Result<()> {
    let args = TakeOfferPermissionlessArgs {
        token_in_amount,
        approval_message: None, // Always None for permissionless
    };

    let mutdata = TAKE_OFFER_PERMISSIONLESS_DISCRIMINATOR.to_vec();
    args.serialize(&mut data)?;

    let accounts = vec![
        AccountMeta::new(offer.key(), false),
        AccountMeta::new_readonly(state.key(), false),
        AccountMeta::new_readonly(boss.key(), false),
        AccountMeta::new_readonly(vault_authority.key(), false),
        AccountMeta::new(vault_token_in_account.key(), false),
        AccountMeta::new(vault_token_out_account.key(), false),
        AccountMeta::new_readonly(permissionless_authority.key(), false),
        AccountMeta::new(permissionless_token_in_account.key(), false),
        AccountMeta::new(permissionless_token_out_account.key(), false),
        AccountMeta::new(token_in_mint.key(), false),
        AccountMeta::new_readonly(token_in_program.key(), false),
        AccountMeta::new(token_out_mint.key(), false),
        AccountMeta::new_readonly(token_out_program.key(), false),
        AccountMeta::new(user_token_in_account.key(), false),
        AccountMeta::new(user_token_out_account.key(), false),
        AccountMeta::new(boss_token_in_account.key(), false),
        AccountMeta::new_readonly(mint_authority.key(), false),
        AccountMeta::new_readonly(instructions_sysvar.key(), false),
        AccountMeta::new(user.key(), true),
        AccountMeta::new_readonly(associated_token_program.key(), false),
        AccountMeta::new_readonly(system_program.key(), false),
    ];

    let ix = solana_program::instruction::Instruction {
        program_id: ONRE_PROGRAM_ID,
        accounts,
        data,
    };

    let account_infos = vec![
        offer,
        state,
        boss,
        vault_authority,
        vault_token_in_account,
        vault_token_out_account,
        permissionless_authority,
        permissionless_token_in_account,
        permissionless_token_out_account,
        token_in_mint,
        token_in_program,
        token_out_mint,
        token_out_program,
        user_token_in_account,
        user_token_out_account,
        boss_token_in_account,
        mint_authority,
        instructions_sysvar,
        user,
        associated_token_program,
        system_program,
        onre_program,
    ];

    match signer_seeds {
        Some(seeds) => solana_program::program::invoke_signed(&ix, &account_infos, seeds),
        None => solana_program::program::invoke(&ix, &account_infos),
    }?;

    Ok(())
}
```

#### Rust PDA Derivation

```rust
use anchor_lang::prelude::*;

let program_id = &onreapp::ID;

// State PDA
let (state_pda, _) = Pubkey::find_program_address(&[b"state"], program_id);

// Offer PDA
let (offer_pda, _) = Pubkey::find_program_address(
    &[b"offer", token_in_mint.as_ref(), token_out_mint.as_ref()],
    program_id
);

// Vault Authority PDA
let (vault_authority_pda, _) = Pubkey::find_program_address(
    &[b"offer_vault_authority"],
    program_id
);

// Permissionless Authority PDA
let (permissionless_authority_pda, _) = Pubkey::find_program_address(
    &[b"permissionless-1"],
    program_id
);

// Mint Authority PDA
let (mint_authority_pda, _) = Pubkey::find_program_address(
    &[b"mint_authority"],
    program_id
);
```

### Support

* GitHub: <https://github.com/onre-finance/onre-sol>
* Telegram / Email:
  * Theodore Georgas: @tgeorgas / <theodore@onre.finance>
  * Nuno Carvalho: @nmcarv / <nuno@onre.finance>
  * Aleksandar Marinkovic: @mankoxyz / <aleksandar@onre.finance>
