Skip to content

Commit

Permalink
Credix implementation (#137)
Browse files Browse the repository at this point in the history
* Implement credix deposit/withdraw
  • Loading branch information
Orelsanpls authored Sep 4, 2022
1 parent 739b786 commit 7654460
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 5 deletions.
91 changes: 91 additions & 0 deletions components/instructions/programs/credix.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { struct, u8, nu64 } from 'buffer-layout';
import { AccountMetaData } from '@solana/spl-governance';
import { Connection } from '@solana/web3.js';
import { CredixConfiguration } from '@tools/sdk/credix/configuration';
import { ANCHOR_DISCRIMINATOR_LAYOUT } from '@utils/helpers';
import { nativeBNToUiAmount } from '@tools/sdk/units';
import { USDC_DECIMALS } from '@uxd-protocol/uxd-client';
import { BN } from '@project-serum/anchor';

export const CREDIX_PROGRAM_INSTRUCTIONS = {
[CredixConfiguration.credixProgramId.toBase58()]: {
[CredixConfiguration.instructionsCode.deposit]: {
name: 'Credix - Deposit USDC',
accounts: [
'Investor',
'Gateway Token',
'Global Market State',
'Signing Authority',
'Investor Token Account',
'Liquidity Pool Token Account',
'LP Token Mint',
'Investor Lp Token Account',
'Credix Pass',
'Base Token Mint',
'Associated Token Program',
'Rent',
'Token Program',
'System Program',
],
getDataUI: async (
_connection: Connection,
data: Uint8Array,
_accounts: AccountMetaData[],
) => {
const dataLayout = struct([
u8('instruction'),
...ANCHOR_DISCRIMINATOR_LAYOUT,
nu64('amount'),
]);

const { amount } = dataLayout.decode(Buffer.from(data)) as any;

const uiAmount = nativeBNToUiAmount(new BN(amount), USDC_DECIMALS);

return <p>{`USDC Amount: ${uiAmount.toString()}`}</p>;
},
},
[CredixConfiguration.instructionsCode.withdraw]: {
name: 'Credix - Withdraw USDC',
accounts: [
'Investor',
'Gateway Token',
'Global Market State',
'Signing Authority',
'Investor Lp Token Account',
'Investor Token Account',
'Liquidity Pool Token Account',
'Treasury Pool Token Account',
'LP TokenMint',
'Credix Pass',
'Base Token Mint',
'Associated Token Program',
'Token Program',
],
getDataUI: async (
_connection: Connection,
data: Uint8Array,
_accounts: AccountMetaData[],
) => {
const dataLayout = struct([
u8('instruction'),
...ANCHOR_DISCRIMINATOR_LAYOUT,
nu64('baseWithdrawalAmount'),
]);

const { baseWithdrawalAmount } = dataLayout.decode(
Buffer.from(data),
) as any;

const uiBaseWithdrawalAmount = nativeBNToUiAmount(
new BN(baseWithdrawalAmount),
USDC_DECIMALS,
);

return (
<p>{`Base USDC Withdrawal Amount: ${uiBaseWithdrawalAmount.toString()}`}</p>
);
},
},
},
};
2 changes: 2 additions & 0 deletions components/instructions/tools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { DELTAFI_PROGRAM_INSTRUCTIONS } from './programs/deltafi';
import { ORCA_PROGRAM_INSTRUCTIONS } from './programs/orca';
import { COMPUTE_BUDGET_INSTRUCTIONS } from './programs/computeBudgetProgram';
import { MERCURIAL_PROGRAM_INSTRUCTIONS } from './programs/mercurial';
import { CREDIX_PROGRAM_INSTRUCTIONS } from './programs/credix';

/**
* Default governance program id instance
Expand Down Expand Up @@ -137,6 +138,7 @@ export const INSTRUCTION_DESCRIPTORS = {
...ORCA_PROGRAM_INSTRUCTIONS,
...COMPUTE_BUDGET_INSTRUCTIONS,
...MERCURIAL_PROGRAM_INSTRUCTIONS,
...CREDIX_PROGRAM_INSTRUCTIONS,
};

export async function getInstructionDescriptor(
Expand Down
16 changes: 16 additions & 0 deletions hooks/useGovernanceAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ export default function useGovernanceAssets() {
name: 'Mercurial',
image: '/img/mercurial.png',
},
[PackageEnum.Credix]: {
name: 'Credix',
image: '/img/credix.jpeg',
},
};

const instructions: Instructions = {
Expand Down Expand Up @@ -681,6 +685,18 @@ export default function useGovernanceAssets() {
packageId: PackageEnum.Native,
tag: 'beta',
},
[InstructionEnum.CredixDeposit]: {
name: 'Deposit',
isVisible: canUseAnyInstruction,
packageId: PackageEnum.Credix,
tag: 'beta',
},
[InstructionEnum.CredixWithdraw]: {
name: 'Withdraw',
isVisible: canUseAnyInstruction,
packageId: PackageEnum.Credix,
tag: 'beta',
},
};

const availableInstructions = Object.entries(instructions)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"dependencies": {
"@blockworks-foundation/mango-client": "^3.3.16",
"@blockworks-foundation/voter-stake-registry-client": "^0.2.0",
"@credix/credix-client": "1.0.0",
"@emotion/react": "^11.1.5",
"@emotion/styled": "^11.3.0",
"@friktion-labs/friktion-sdk": "1.1.113",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as yup from 'yup';
import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder';
import credixConfiguration from '@tools/sdk/credix/configuration';
import { GovernedMultiTypeAccount, tryGetMint } from '@utils/tokens';
import { CredixDepositForm } from '@utils/uiTypes/proposalCreationTypes';
import Input from '@components/inputs/Input';
import { uiAmountToNativeBN } from '@tools/sdk/units';

const schema = yup.object().shape({
governedAccount: yup
.object()
.nullable()
.required('Governed account is required'),
uiAmount: yup
.number()
.typeError('Amount has to be a number')
.required('Amount is required'),
});

const CredixDeposit = ({
index,
governedAccount,
}: {
index: number;
governedAccount?: GovernedMultiTypeAccount;
}) => {
const {
form,
handleSetForm,
formErrors,
} = useInstructionFormBuilder<CredixDepositForm>({
index,
initialFormValues: {
governedAccount,
},
schema,
buildInstruction: async function ({
cluster,
governedAccountPubkey,
form,
connection,
wallet,
}) {
if (cluster !== 'mainnet') {
throw new Error('Other cluster than mainnet are not supported yet.');
}

const client = credixConfiguration.getClient({
connection,
wallet: (wallet as unknown) as any,
});

// the market for which we want to deposit in the liquidity pool
const market = await client.fetchMarket('credix-marketplace');

if (!market) {
throw new Error(
'Cannot load market information about credix-marketplace',
);
}

const marketMintInfo = await tryGetMint(connection, market.baseMintPK);

if (!marketMintInfo) {
throw new Error(
'Cannot load information about credix market base mint',
);
}

const amount = uiAmountToNativeBN(
form.uiAmount!,
marketMintInfo.account.decimals,
);

return market.depositIx(amount.toNumber(), governedAccountPubkey);
},
});

return (
<Input
min={0}
label="USDC Amount"
value={form.uiAmount}
type="number"
onChange={(evt) => {
handleSetForm({
value: evt.target.value,
propertyName: 'uiAmount',
});
}}
error={formErrors['uiAmount']}
/>
);
};

export default CredixDeposit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as yup from 'yup';
import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder';
import credixConfiguration from '@tools/sdk/credix/configuration';
import { GovernedMultiTypeAccount, tryGetMint } from '@utils/tokens';
import { CredixWithdrawForm } from '@utils/uiTypes/proposalCreationTypes';
import Input from '@components/inputs/Input';
import { uiAmountToNativeBN } from '@tools/sdk/units';

const schema = yup.object().shape({
governedAccount: yup
.object()
.nullable()
.required('Governed account is required'),
uiAmount: yup
.number()
.typeError('Amount has to be a number')
.required('Amount is required'),
});

const CredixWithdraw = ({
index,
governedAccount,
}: {
index: number;
governedAccount?: GovernedMultiTypeAccount;
}) => {
const {
form,
handleSetForm,
formErrors,
} = useInstructionFormBuilder<CredixWithdrawForm>({
index,
initialFormValues: {
governedAccount,
},
schema,
buildInstruction: async function ({
cluster,
governedAccountPubkey,
form,
connection,
wallet,
}) {
if (cluster !== 'mainnet') {
throw new Error('Other cluster than mainnet are not supported yet.');
}

const client = credixConfiguration.getClient({
connection,
wallet: (wallet as unknown) as any,
});

// the market for which we want to deposit in the liquidity pool
const market = await client.fetchMarket('credix-marketplace');

if (!market) {
throw new Error(
'Cannot load market information about credix-marketplace',
);
}

const marketMintInfo = await tryGetMint(connection, market.baseMintPK);

if (!marketMintInfo) {
throw new Error(
'Cannot load information about credix market base mint',
);
}

const amount = uiAmountToNativeBN(
form.uiAmount!,
marketMintInfo.account.decimals,
);

return market.withdrawIx(amount.toNumber(), governedAccountPubkey);
},
});

return (
<Input
min={0}
label="USDC Amount"
value={form.uiAmount}
type="number"
onChange={(evt) => {
handleSetForm({
value: evt.target.value,
propertyName: 'uiAmount',
});
}}
error={formErrors['uiAmount']}
/>
);
};

export default CredixWithdraw;
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ import OrcaWhirlpoolSwap from './Orca/WhirlpoolSwap';
import MercurialPoolDeposit from './Mercurial/PoolDeposit';
import MercurialPoolWithdraw from './Mercurial/PoolWithdraw';
import NativeIncreaseComputingBudget from './Native/IncreaseComputingBudget';
import CredixDeposit from './Credix/Deposit';
import CredixWithdraw from './Credix/Withdraw';

const SelectedInstruction = ({
itxType,
Expand Down Expand Up @@ -564,6 +566,10 @@ const SelectedInstruction = ({
governedAccount={governedAccount}
/>
);
case InstructionEnum.CredixDeposit:
return <CredixDeposit index={index} governedAccount={governedAccount} />;
case InstructionEnum.CredixWithdraw:
return <CredixWithdraw index={index} governedAccount={governedAccount} />;
default:
return null;
}
Expand Down
Binary file added public/img/credix.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions tools/sdk/credix/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { CredixClient } from '@credix/credix-client';
import { Wallet } from '@marinade.finance/marinade-ts-sdk';
import { Connection, PublicKey } from '@solana/web3.js';

export class CredixConfiguration {
public static readonly credixProgramId = new PublicKey(
'CRDx2YkdtYtGZXGHZ59wNv1EwKHQndnRc1gT4p8i2vPX',
);

public static readonly instructionsCode = {
deposit: 202,
withdraw: 241,
};

public getClient({
connection,
wallet,
}: {
connection: Connection;
wallet: Wallet;
}): CredixClient {
return new CredixClient(connection, wallet, {
programId: CredixConfiguration.credixProgramId,
});
}
}

export default new CredixConfiguration();
Loading

1 comment on commit 7654460

@vercel
Copy link

@vercel vercel bot commented on 7654460 Sep 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.