credix_client 0.9.1

Crate to help perform CPI calls to the Credix program
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
# Credix Rust client

A crate to interact with the Credix program via CPI

*NOTE*: This crate was generated with the help of the Anchor generated IDL and adds a few extra functions in addition to the generated code.
To know certain pda seeds or which accounts are mutable, please refer back to the Credix program IDL. It is available for each environment in the source of this crate.

## Environments

### Pre-mainnet

- Pre-mainnet: This is the Solana devnet testing environment for Credix programs. It is usually very close to the mainnet environment. Before releasing any feature to mainnet, the pre-mainnet program is updated.
Address: `crdRi38zEhQdzpsxnKur73WHBM9BSvXMSfGcbLyJCdP`.

#### Markets

- Market with seed `credix-marketplace` on pre-mainnet should be targeted for testing without withdraw epochs.
- Market with seed `withdraw-epochs` on pre-mainnet should be targeted for testing with withdraw epochs.

#### Configuring the client

To target our pre-mainnet environment you can enable the `pre-mainnet` feature

```toml
credix_client = { version="0.8.0", features = ["pre-mainnet"], default-features = false }
```

### Mainnet

- Mainnet: This is our production environment.
Address: `CRDx2YkdtYtGZXGHZ59wNv1EwKHQndnRc1gT4p8i2vPX`

*Note*: To use any market on any Credix environment, you will need an active Credix pass with certain permissions set. Contact Credix to have one issued.

#### Configuring the client

To target our mainnet environment you can enable the `mainnet` feature

```toml
credix_client = { version="0.8.0", features = ["mainnet"], default-features = false }
```

## Local development:

### Getting the Credix binary

When creating a local setup it's advised to grab the pre-mainnet binary from the Solana devnet cluster when preparing for upcoming Credix mainnet releases.
This can be done using following command:

```sh
solana program dump -u d crdRi38zEhQdzpsxnKur73WHBM9BSvXMSfGcbLyJCdP ./credix.so
```

Day to day stable testing can be done by grabbing the mainnet binary using following command:

```sh
solana program dump -u m CRDx2YkdtYtGZXGHZ59wNv1EwKHQndnRc1gT4p8i2vPX ./credix.so

```

### Preparing the local cluster

To get to a working local environment certain instructions have to be called to set up everything.
The following instructions should be called in the same order.

You can find more [cargo docs](https://docs.rs/credix_client/latest/credix_client/instruction/index.html) information for each instruction.

You can find more [cargo docs](https://docs.rs/credix_client/latest/credix_client/state/index.html) information for each account.

#### Credix program preparation

##### `initialize_program_state`

Initializes the ProgramState account that is shared across the entire Credix ecosystem for management purposes.
This will allow you to create markets.

##### `initialize_market`

Creates a market.

##### `issue_credix_pass`

This gives permissions to any participant in the market. It can only be issued by either a key that is listed as a pass issuer in the MarketAdmins account or by the multisig specified in the ProgramState.

Any user interaction with a market requires an active Credix pass.

#### Market interactions

##### `deposit_funds`

This deposits funds into the pool in exchange for LP tokens.

##### `create_withdraw_epoch`

Creates a withdraw epoch. This needs to happen before anyone can participate in an epoch. The epoch needs to be created when the previous one is completely over.

##### `create_withdraw_request`

Creates a withdraw request. This is where you request a certain amount to withdraw. Depending on the amounts requested by other participants in the epoch and the money available in the market pool, a certain amount will be made available in a first instance. Then a second round happens where amounts that weren't withdrawn become available to the rest of the participants.
The duration of the different phases and other parameters are specified on the market level.

##### `redeem_request`

Here we actually withdraw funds during either the redeem phase or the free for all phase.

#### Affecting the LP price

So far no interest repayments have happened so no change in the LP price has occurred.
To include this scenario in your tests you need to do a few steps.
Again, following instructions need to be done in order and before the withdraw epoch setup to see the effect and more information can be found in the same places as described before.

For convenience's sake we will provide some basic configurations to use to be able to trigger an interest repayment.

##### `create_deal`

Creates a deal.

###### Example config

```rust
    max_funding_duration: 255,
    deal_name: "Simple Deal",
    arrangement_fees: 0,
    arrangement_fee_percentage: Fraction::new(0,100),
    migrated: false,
```

##### `set_repayment_schedule`

Sets the repayment schedule. This defines when repayments need to happen and according to what waterfall they need to be processed.

###### Example config

```rust
let now = system_time::now();
// now will contain the current unix timestamp in ms.
let principal = 1000000;

let config_for_instruction= RepaymentScheduleConfig {
            periods: vec![
                RepaymentPeriodInput {
                    waterfall_index: 0,
                    accrual_in_days: 30,
                    principal_expected: None,
                    time_frame: TimeFrame {
                        start: now,
                        end: now + 30 * SECONDS_IN_DAY - 1,
                    },
                },
                RepaymentPeriodInput {
                    waterfall_index: 1,
                    accrual_in_days: 30,
                   principal_expected: Some(principal),
                    time_frame: TimeFrame {
                        start: now + 30 * SECONDS_IN_DAY,
                        end: now + 60 * SECONDS_IN_DAY - 1,
                    },
                }
            ],
            start_ts: now,
            daycount_convention: DaycountConvention::Act360,
            waterfall_definitions: vec![
                DistributionWaterfall {
                    waterfall_type: DistributionWaterfallType::Revolving,
                    tiers: vec![
                        WaterfallTier {
                            allocations: vec![RepaymentAllocation::Interest],
                            tranche_indices: vec![0],
                            charge: true,
                            slash: false,
                        },
                    ],
                },
                DistributionWaterfall {
                    waterfall_type: DistributionWaterfallType::Amortization,
                    tiers: vec![
                        WaterfallTier {
                            allocations: vec![
                             RepaymentAllocation::Principal,
                             RepaymentAllocation::Interest
                            ],
                            tranche_indices: vec![0],
                            charge: true,
                            slash: false,
                        }
                    ],
                },
            ],
    };
```

##### `set_tranches`

Here we define the composition of the deal in tranches. Tranches are the investment opportunities of a deal.

###### Example config

This includes a tranche that is funded by the pool. This means that when the deal is activated, money will flow from the market's liquidity pool towards the deal.

```rust
vec![
    TrancheConfig {
        index: 0,
        max_deposit_percentage: Fraction::new(1, 1).unwrap(),
        early_withdrawal_principal: true,
        funded_by_liquidity_pool: true,
        name: "A".to_string(),
        size: principal, // the variable we declared earlier
        interest: Fraction::new(12, 10).unwrap(),
        interest_performance_fee: Fraction::new(1, 10).unwrap(),
        principal_performance_fee: Fraction::new(0, 1).unwrap(),
        membership_fee: Fraction::new(1, 10).unwrap(),
        late_interest: Fraction::new(1, 10).unwrap(),
        early_principal: Fraction::new(1, 10).unwrap(),
        late_principal: Fraction::new(1, 10).unwrap(),
    },
],
```

##### `open_deal`

Once a deal has been fully configured, we have to change it's status to allow investors to invest into it. This opens the deal for funding.

##### `activate_deal`

Once a deal has been fully funded (which is automatically the case using the configs we provided), a deal needs to be activated. This marks the moment funds move from the liquidity pool towards the deal to fund the pool funded tranches.
The actual borrowed amount becomes available to the borrower.

##### `repay_deal`

This allows the borrower to repay the deal. At this point interest will flow towards the pool and the LP price should rise and your investment should have RoI.

## Getting the LP price

The LP token price is the TVL divided by the total supply of LP tokens. The TVL is equal to the total outstanding credit and the funds in the liquidity pool token account.

It can be found on-chain using following calculation:

```rust
let pool_liquidity = liquidity_pool_token_account.amount;
let outstanding_credit = global_market_state.pool_outstanding_credit;
let tvl = pool_liquidity + outstanding_credit;

let lp_supply = lp_token_mint.supply;

let lp_price = tvl / lp_supply;
```

## Examples

### On-chain

This is an example rust program made with anchor, here we have CPI for deposit, withdraw, create withdraw request and redeem withdraw request.

```Rust
use anchor_lang::prelude::*;
use credix_client::cpi::accounts::{
    CreateWithdrawRequest, DepositFunds, RedeemWithdrawRequest, WithdrawFunds,
};
use credix_client::cpi::{
    create_withdraw_request, deposit_funds, redeem_withdraw_request, withdraw_funds,
};

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

#[program]
pub mod integrator {
    use super::*;

    pub fn deposit_cpi(ctx: Context<DepositFundsCpi>, amount: u64) -> Result<()> {
        let cpi_context = CpiContext::new(
            ctx.accounts.credix_program.to_account_info(),
            DepositFunds {
                investor: ctx.accounts.investor.to_account_info(),
                global_market_state: ctx.accounts.global_market_state.to_account_info(),
                signing_authority: ctx.accounts.signing_authority.to_account_info(),
                investor_token_account: ctx.accounts.investor_token_account.to_account_info(),
                credix_pass: ctx.accounts.credix_pass.to_account_info(),
                investor_lp_token_account: ctx.accounts.investor_lp_token_account.to_account_info(),
                liquidity_pool_token_account: ctx
                    .accounts
                    .liquidity_pool_token_account
                    .to_account_info(),
                lp_token_mint: ctx.accounts.lp_token_mint.to_account_info(),
                rent: ctx.accounts.rent.to_account_info(),
                system_program: ctx.accounts.system_program.to_account_info(),
                token_program: ctx.accounts.token_program.to_account_info(),
                associated_token_program: ctx.accounts.associated_token_program.to_account_info(),
                base_token_mint: ctx.accounts.base_token_mint.to_account_info(),
            },
        );

        deposit_funds(cpi_context, amount)?;

        Ok(())
    }

    pub fn withdraw_cpi(ctx: Context<WithdrawFundsCpi>, amount: u64) -> Result<()> {
        let cpi_context = CpiContext::new(
            ctx.accounts.credix_program.to_account_info(),
            WithdrawFunds {
                investor: ctx.accounts.investor.to_account_info(),
                global_market_state: ctx.accounts.global_market_state.to_account_info(),
                signing_authority: ctx.accounts.signing_authority.to_account_info(),
                investor_lp_token_account: ctx.accounts.investor_lp_token_account.to_account_info(),
                investor_token_account: ctx.accounts.investor_token_account.to_account_info(),
                liquidity_pool_token_account: ctx
                    .accounts
                    .liquidity_pool_token_account
                    .to_account_info(),
                lp_token_mint: ctx.accounts.lp_token_mint.to_account_info(),
                program_state: ctx.accounts.program_state.to_account_info(),
                credix_treasury: ctx.accounts.credix_treasury.to_account_info(),
                credix_treasury_token_account: ctx
                    .accounts
                    .credix_treasury_token_account
                    .to_account_info(),
                treasury_pool_token_account: ctx
                    .accounts
                    .treasury_pool_token_account
                    .to_account_info(),
                credix_pass: ctx.accounts.credix_pass.to_account_info(),
                base_token_mint: ctx.accounts.base_token_mint.to_account_info(),
                associated_token_program: ctx.accounts.associated_token_program.to_account_info(),
                token_program: ctx.accounts.token_program.to_account_info(),
                rent: ctx.accounts.rent.to_account_info(),
                system_program: ctx.accounts.system_program.to_account_info(),
            },
        );

        withdraw_funds(cpi_context, amount)?;

        Ok(())
    }

    pub fn create_withdraw_request_cpi(
        ctx: Context<WithdrawRequestCpi>,
        amount: u64,
    ) -> Result<()> {
        let accounts = ctx.accounts;
        let cpi_context = CpiContext::new(
            accounts.credix.to_account_info(),
            CreateWithdrawRequest {
                payer: accounts.payer.to_account_info(),
                investor: accounts.investor.to_account_info(),
                credix_pass: accounts.credix_pass.to_account_info(),
                global_market_state: accounts.global_market_state.to_account_info(),
                investor_lp_token_account: accounts.investor_lp_token_account.to_account_info(),
                liquidity_pool_token_account: accounts
                    .liquidity_pool_token_account
                    .to_account_info(),
                lp_token_mint: accounts.lp_token_mint.to_account_info(),
                signing_authority: accounts.signing_authority.to_account_info(),
                system_program: accounts.system_program.to_account_info(),
                withdraw_epoch: accounts.withdraw_epoch.to_account_info(),
            },
        );

        create_withdraw_request(cpi_context, amount)
    }

    pub fn redeem_request_cpi(ctx: Context<RedeemRequestCpi>, amount: u64) -> Result<()> {
        let accounts = ctx.accounts;
        let cpi_context = CpiContext::new(
            accounts.credix.to_account_info(),
            RedeemWithdrawRequest {
                investor: accounts.investor.to_account_info(),
                credix_pass: accounts.credix_pass.to_account_info(),
                global_market_state: accounts.global_market_state.to_account_info(),
                investor_lp_token_account: accounts.investor_lp_token_account.to_account_info(),
                liquidity_pool_token_account: accounts
                    .liquidity_pool_token_account
                    .to_account_info(),
                lp_token_mint: accounts.lp_token_mint.to_account_info(),
                signing_authority: accounts.signing_authority.to_account_info(),
                system_program: accounts.system_program.to_account_info(),
                withdraw_epoch: accounts.withdraw_epoch.to_account_info(),
                associated_token_program: accounts.associated_token_program.to_account_info(),
                base_token_mint: accounts.base_token_mint.to_account_info(),
                credix_treasury: accounts.credix_treasury.to_account_info(),
                credix_treasury_token_account: accounts
                    .credix_treasury_token_account
                    .to_account_info(),
                program_state: accounts.program_state.to_account_info(),
                investor_token_account: accounts.investor_token_account.to_account_info(),
                rent: accounts.rent.to_account_info(),
                token_program: accounts.token_program.to_account_info(),
                treasury_pool_token_account: accounts.treasury_pool_token_account.to_account_info(),
            },
        );

        redeem_withdraw_request(cpi_context, amount)
    }
}

#[derive(Accounts)]
pub struct DepositFundsCpi<'info> {
    #[account(mut)]
    pub investor: Signer<'info>,
    pub credix_program: Program<'info, Credix>,
    /// CHECK: test program
    pub global_market_state: AccountInfo<'info>,
    /// CHECK: test program
    pub signing_authority: AccountInfo<'info>,
    /// CHECK: test program
    #[account(mut)]
    pub investor_token_account: AccountInfo<'info>,
    /// CHECK: test program
    #[account(mut)]
    pub liquidity_pool_token_account: AccountInfo<'info>,
    /// CHECK: test program
    #[account(mut)]
    pub lp_token_mint: AccountInfo<'info>,
    /// CHECK: test program
    #[account(mut)]
    pub investor_lp_token_account: AccountInfo<'info>,
    /// CHECK: test program
    pub credix_pass: AccountInfo<'info>,
    /// CHECK: test program
    pub base_token_mint: AccountInfo<'info>,
    /// CHECK: test program
    pub associated_token_program: AccountInfo<'info>,
    pub rent: Sysvar<'info, Rent>,
    /// CHECK: test program
    pub token_program: AccountInfo<'info>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct WithdrawFundsCpi<'info> {
    #[account(mut)]
    pub investor: Signer<'info>,
    pub credix_program: Program<'info, Credix>,
    #[account(mut)]
    /// CHECK: test program
    pub global_market_state: AccountInfo<'info>,
    /// CHECK: test program
    pub program_state: AccountInfo<'info>,
    /// CHECK: test program
    pub signing_authority: AccountInfo<'info>,
    #[account(mut)]
    /// CHECK: test program
    pub investor_lp_token_account: AccountInfo<'info>,
    #[account(mut)]
    /// CHECK: test program
    pub investor_token_account: AccountInfo<'info>,
    #[account(mut)]
    /// CHECK: test program
    pub liquidity_pool_token_account: AccountInfo<'info>,
    /// CHECK: test program
    pub credix_treasury: AccountInfo<'info>,
    #[account(mut)]
    /// CHECK: test program
    pub credix_treasury_token_account: AccountInfo<'info>,
    #[account(mut)]
    /// CHECK: test program
    pub treasury_pool_token_account: AccountInfo<'info>,
    #[account(mut)]
    /// CHECK: test program
    pub lp_token_mint: AccountInfo<'info>,
    #[account(mut)]
    /// CHECK: test program
    pub credix_pass: AccountInfo<'info>,
    /// CHECK: test program
    pub base_token_mint: AccountInfo<'info>,
    /// CHECK: test program
    pub associated_token_program: AccountInfo<'info>,
    /// CHECK: test program
    pub token_program: AccountInfo<'info>,
    pub rent: Sysvar<'info, Rent>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct WithdrawRequestCpi<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    #[account(mut)]
    pub investor: Signer<'info>,
    /// CHECK:
    pub global_market_state: AccountInfo<'info>,
    /// CHECK:
    pub signing_authority: AccountInfo<'info>,
    /// CHECK:
    #[account()]
    pub credix_pass: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub withdraw_epoch: AccountInfo<'info>,
    /// CHECK:
    #[account()]
    pub investor_lp_token_account: AccountInfo<'info>,
    /// CHECK:
    #[account()]
    pub liquidity_pool_token_account: AccountInfo<'info>,
    /// CHECK:
    #[account()]
    pub lp_token_mint: AccountInfo<'info>,
    pub credix: Program<'info, Credix>,
    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct RedeemRequestCpi<'info> {
    #[account(mut)]
    pub investor: Signer<'info>,
    /// CHECK:
    #[account(mut)]
    pub global_market_state: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub withdraw_epoch: AccountInfo<'info>,
    /// CHECK:
    #[account()]
    pub program_state: AccountInfo<'info>,
    /// CHECK: The check happens when verifying the PDA address.
    #[account()]
    pub signing_authority: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub investor_lp_token_account: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub investor_token_account: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub liquidity_pool_token_account: AccountInfo<'info>,
    /// CHECK: The address of account is known.
    #[account()]
    pub credix_treasury: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub credix_treasury_token_account: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub treasury_pool_token_account: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub lp_token_mint: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    pub credix_pass: AccountInfo<'info>,
    /// CHECK:
    #[account()]
    pub base_token_mint: AccountInfo<'info>,
    /// CHECK:
    pub associated_token_program: AccountInfo<'info>,
    /// CHECK:
    pub token_program: AccountInfo<'info>,
    pub rent: Sysvar<'info, Rent>,
    pub system_program: Program<'info, System>,
    pub credix: Program<'info, Credix>,
}

```

### Off-chain

#### Typescript

For off-chain development we provide a [Typescript client](https://www.npmjs.com/package/@credix/credix-client) to help with gathering different accounts. See the README of that package to get started with it.

```typescript
...
const marketName = "credix-marketplace";
const market = await client.fetchMarket(marketName);
 Our client can help you with finding the keys of following accounts
const globalMarketState = market.address;
const programState = (await client.fetchProgramState()).address;
const signingAuthority = (await market.generateSigningAuthorityPDA())[0];
const investorLpTokenAccount = await market.findLPTokenAccount(investor);
const investorTokenAccount = await market.findBaseTokenAccount(investor);
const liquidityPoolTokenAccount = await market.findLiquidityPoolTokenAccount();
const credixTreasury = (await client.fetchProgramState()).credixTreasury;
const credixTreasuryTokenAccount = await market.findBaseTokenAccount(credixTreasury);
const treasuryPoolTokenAccount = market.treasury;
const lpTokenMint = market.lpMintPK;
const credixPass = (await market.fetchCredixPass(investor)).address
const baseTokenMint = await market.baseMintPK;

// Withdraw Epoch related accounts
const market = await client.fetchMarket(marketName);
const withdrawEpochAddress = WithdrawEpoch.generatePDA(market, market.latestWithdrawEpochIdx);
...
```

#### Rust

This crate also provides functions to help generate PDA's. See the Rust [docs](https://docs.rs/credix_client/latest/credix_client/state/)

### Disclaimer

These examples are provided as is. Do not blindly copy paste for use in production.