diff --git a/components/instructions/programs/credix.tsx b/components/instructions/programs/credix.tsx
new file mode 100644
index 0000000000..9d283c4111
--- /dev/null
+++ b/components/instructions/programs/credix.tsx
@@ -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
{`USDC Amount: ${uiAmount.toString()}`}
;
+ },
+ },
+ [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 (
+ {`Base USDC Withdrawal Amount: ${uiBaseWithdrawalAmount.toString()}`}
+ );
+ },
+ },
+ },
+};
diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx
index f33bc5c663..ece95a23ef 100644
--- a/components/instructions/tools.tsx
+++ b/components/instructions/tools.tsx
@@ -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
@@ -137,6 +138,7 @@ export const INSTRUCTION_DESCRIPTORS = {
...ORCA_PROGRAM_INSTRUCTIONS,
...COMPUTE_BUDGET_INSTRUCTIONS,
...MERCURIAL_PROGRAM_INSTRUCTIONS,
+ ...CREDIX_PROGRAM_INSTRUCTIONS,
};
export async function getInstructionDescriptor(
diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts
index 04328f64bc..3316808694 100644
--- a/hooks/useGovernanceAssets.ts
+++ b/hooks/useGovernanceAssets.ts
@@ -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 = {
@@ -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)
diff --git a/package.json b/package.json
index 51e169be4f..145b290745 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/pages/dao/[symbol]/proposal/components/instructions/Credix/Deposit.tsx b/pages/dao/[symbol]/proposal/components/instructions/Credix/Deposit.tsx
new file mode 100644
index 0000000000..74f57019a1
--- /dev/null
+++ b/pages/dao/[symbol]/proposal/components/instructions/Credix/Deposit.tsx
@@ -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({
+ 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 (
+ {
+ handleSetForm({
+ value: evt.target.value,
+ propertyName: 'uiAmount',
+ });
+ }}
+ error={formErrors['uiAmount']}
+ />
+ );
+};
+
+export default CredixDeposit;
diff --git a/pages/dao/[symbol]/proposal/components/instructions/Credix/Withdraw.tsx b/pages/dao/[symbol]/proposal/components/instructions/Credix/Withdraw.tsx
new file mode 100644
index 0000000000..1bef944afb
--- /dev/null
+++ b/pages/dao/[symbol]/proposal/components/instructions/Credix/Withdraw.tsx
@@ -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({
+ 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 (
+ {
+ handleSetForm({
+ value: evt.target.value,
+ propertyName: 'uiAmount',
+ });
+ }}
+ error={formErrors['uiAmount']}
+ />
+ );
+};
+
+export default CredixWithdraw;
diff --git a/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx b/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx
index 33758dd88d..0c077c8039 100644
--- a/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx
+++ b/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx
@@ -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,
@@ -564,6 +566,10 @@ const SelectedInstruction = ({
governedAccount={governedAccount}
/>
);
+ case InstructionEnum.CredixDeposit:
+ return ;
+ case InstructionEnum.CredixWithdraw:
+ return ;
default:
return null;
}
diff --git a/public/img/credix.jpeg b/public/img/credix.jpeg
new file mode 100644
index 0000000000..238019d710
Binary files /dev/null and b/public/img/credix.jpeg differ
diff --git a/tools/sdk/credix/configuration.ts b/tools/sdk/credix/configuration.ts
new file mode 100644
index 0000000000..b8cb81b0a3
--- /dev/null
+++ b/tools/sdk/credix/configuration.ts
@@ -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();
diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts
index 4467c5ea80..79f22eaab2 100644
--- a/utils/uiTypes/proposalCreationTypes.ts
+++ b/utils/uiTypes/proposalCreationTypes.ts
@@ -608,6 +608,16 @@ export interface NativeIncreaseComputingBudgetForm {
computingBudget?: number;
}
+export interface CredixDepositForm {
+ governedAccount?: GovernedMultiTypeAccount;
+ uiAmount?: number;
+}
+
+export interface CredixWithdrawForm {
+ governedAccount?: GovernedMultiTypeAccount;
+ uiAmount?: number;
+}
+
export enum InstructionEnum {
Transfer,
ProgramUpgrade,
@@ -693,6 +703,8 @@ export enum InstructionEnum {
MercurialPoolDeposit,
MercurialPoolWithdraw,
NativeIncreaseComputingBudget,
+ CredixDeposit,
+ CredixWithdraw,
}
export enum PackageEnum {
@@ -712,6 +724,7 @@ export enum PackageEnum {
Deltafi,
Orca,
Mercurial,
+ Credix,
}
export type createParams = [
diff --git a/yarn.lock b/yarn.lock
index 15c85cd3f8..76bb28a226 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -638,6 +638,20 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
+"@credix/credix-client@1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@credix/credix-client/-/credix-client-1.0.0.tgz#5275f2f6cf9719a081dc5edc909646ab62ea4ab0"
+ integrity sha512-QZ58Y2YdP4fWdXkIK9qvWngX87pEcbqe1hCcHLPfrLpQlWOXj58HOAf+971tcW7f6GyA5+Z4TtcPi00jIkZQ5w==
+ dependencies:
+ "@identity.com/solana-gateway-ts" "^0.8.2"
+ "@project-serum/anchor" "^0.24.2"
+ "@saberhq/anchor-contrib" "^1.13.32"
+ "@solana/spl-token" "^0.1.8"
+ "@solana/web3.js" "^1.43.4"
+ big.js "^6.1.1"
+ bn.js "^5.2.0"
+ node-irr "^2.0.3"
+
"@cspotcode/source-map-consumer@0.8.0":
version "0.8.0"
resolved "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz"
@@ -905,6 +919,17 @@
resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+"@identity.com/solana-gateway-ts@^0.8.2":
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/@identity.com/solana-gateway-ts/-/solana-gateway-ts-0.8.2.tgz#a8de5929e94a98b64cbdc38b4745f3fb20ae58aa"
+ integrity sha512-xFFoZtlF9XLHZ/ZS59QdVTDAtG7GJU2CqpXzwHwG7bPhhhXJ2zTHqlJ9oax2Faf9UKjyzp7F7TWfMScpTeOkEQ==
+ dependencies:
+ "@solana/web3.js" "^1.22.0"
+ async-retry "^1.3.3"
+ bn.js "^5.2.0"
+ borsh "^0.4.0"
+ ramda "^0.27.1"
+
"@isaacs/string-locale-compare@^1.0.1", "@isaacs/string-locale-compare@^1.1.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz"
@@ -2674,6 +2699,28 @@
superstruct "^0.14.2"
tweetnacl "^1.0.0"
+"@solana/web3.js@^1.43.4":
+ version "1.56.2"
+ resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.56.2.tgz#5212e8b147ebc216ea5a7aa99d5b555ebe41f9bd"
+ integrity sha512-ByWfNA8H/1EB4g0749uhkQ0zZZAQealzRmmT3UMIv3xe0DeHwnrzQUavBtAlHNMrKqLHu8kd+XtPci6zreMjjA==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ "@noble/ed25519" "^1.7.0"
+ "@noble/hashes" "^1.1.2"
+ "@noble/secp256k1" "^1.6.3"
+ "@solana/buffer-layout" "^4.0.0"
+ bigint-buffer "^1.1.5"
+ bn.js "^5.0.0"
+ borsh "^0.7.0"
+ bs58 "^4.0.1"
+ buffer "6.0.1"
+ fast-stable-stringify "^1.0.0"
+ jayson "^3.4.4"
+ js-sha3 "^0.8.0"
+ node-fetch "2"
+ rpc-websockets "^7.5.0"
+ superstruct "^0.14.2"
+
"@solendprotocol/solend-sdk@^0.4.9":
version "0.4.9"
resolved "https://registry.npmjs.org/@solendprotocol/solend-sdk/-/solend-sdk-0.4.9.tgz"
@@ -3669,6 +3716,13 @@ async-limiter@~1.0.0:
resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+async-retry@^1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280"
+ integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==
+ dependencies:
+ retry "0.13.1"
+
async@^2.6.2:
version "2.6.3"
resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz"
@@ -9390,6 +9444,11 @@ node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz"
+node-irr@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/node-irr/-/node-irr-2.0.3.tgz#c7409c5f10dcef9ccb0aace29203700804b4c8aa"
+ integrity sha512-hNPnizd9LLnvQ3zyCGM31+TjVLIdAvtiJCDcKqmiGCPTUGfi+Kyzfi37oXlyMSkV5cIb6ZHoaReLjBtG8I+t+Q==
+
node-modules-regexp@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz"
@@ -10815,6 +10874,11 @@ quick-lru@^5.1.1:
resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
+ramda@^0.27.1:
+ version "0.27.2"
+ resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.2.tgz#84463226f7f36dc33592f6f4ed6374c48306c3f1"
+ integrity sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==
+
range-parser@^1.2.1, range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz"
@@ -11254,16 +11318,16 @@ retry-axios@^2.6.0:
resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-2.6.0.tgz#d4dc5c8a8e73982e26a705e46a33df99a28723e0"
integrity sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ==
+retry@0.13.1, retry@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz"
+ integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
+
retry@^0.12.0:
version "0.12.0"
resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz"
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
-retry@^0.13.1:
- version "0.13.1"
- resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz"
- integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
-
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz"